Redirect standard input dynamically in a bash script

前端 未结 7 793
忘了有多久
忘了有多久 2020-12-13 13:56

I was trying to do this to decide whether to redirect stdin to a file or not:

[ ...some condition here... ] && input=$fileName || input=\"&0\"
./         


        
相关标签:
7条回答
  • 2020-12-13 14:32

    people show to you very long scripts, but.... you get bash trap :) You must quote everything in bash. for example, you want list file named &0 .

    filename='&0' #right ls $filename #wrong! this substitute $filename and interpret &0 ls "$filename" #right

    another, files with spaces.

    filename=' some file with spaces ' ls $filename #wrong, bash cut first and last space, and reduce multiple spaces between with and spaces words ls "$filename" righ

    the same is in your script. please change:

    ./myScript < $input
    

    to

    ./myScript < "$input"
    

    its all. bash has more traps. I suggest make quotation for "$file" with the same reason. spaces and other characters than can be interpreted are allways make problems.

    but what about /dev/stdin ? this is useable only when you redirected stdin and want to print something to real stdin.

    so, your script should show like this:

    [ ...some condition here... ] && input="$fileName" || input="&0"
    ./myScript < "$input"
    
    0 讨论(0)
  • 2020-12-13 14:34

    If you're careful, you can use 'eval' and your first idea.

    [ ...some condition here... ] && input=$fileName || input="&1"
    eval ./myScript < $input
    

    However, you say that 'myScript' is actually a complex command invocation; if it involves arguments which might contain spaces, then you must be very careful before deciding to use 'eval'.

    Frankly, worrying about the cost of a 'cat' command is probably not worth the trouble; it is unlikely to be the bottleneck.

    Even better is to design myScript so that it works like a regular Unix filter - it reads from standard input unless it is given one or more files to work (like, say, cat or grep as examples). That design is based on long and sound experience - and is therefore worth emulating to avoid having to deal with problems such as this.

    0 讨论(0)
  • 2020-12-13 14:40

    Use eval:

    #! /bin/bash
    
    [ $# -gt 0 ] && input="'"$1"'" || input="&1"
    
    eval "./myScript <$input"
    

    This simple stand-in for myScript

    #! /usr/bin/perl -lp
    $_ = reverse
    

    produces the following output:

    $ ./myDemux myScript
    pl- lrep/nib/rsu/ !#
    esrever = _$
    
    $ ./myDemux
    foo
    oof
    bar
    rab
    baz
    zab
    

    Note that it handles spaces in inputs too:

    $ ./myDemux foo\ bar
    eman eht ni ecaps a htiw elif
    

    To pipe input down to myScript, use process substitution:

    $ ./myDemux <(md5sum /etc/issue)
    eussi/cte/  01672098e5a1807213d5ba16e00a7ad0
    

    Note that if you try to pipe the output directly, as in

    $ md5sum /etc/issue | ./myDemux
    

    it will hang waiting on input from the terminal, whereas ephemient's answer does not have this shortcoming.

    A slight change produces the desired behavior:

    #! /bin/bash
    
    [ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
    eval "./myScript <$input"
    
    0 讨论(0)
  • 2020-12-13 14:43

    First of all stdin is file descriptor 0 (zero) rather than 1 (which is stdout).

    You can duplicate file descriptors or use filenames conditionally like this:

    [[ some_condition ]] && exec 3<"$filename" || exec 3<&0
    
    some_long_command_line <&3
    

    Note that the command shown will execute the second exec if either the condition is false or the first exec fails. If you don't want a potential failure to do that then you should use an if / else:

    if [[ some_condition ]]
    then
        exec 3<"$filename"
    else
        exec 3<&0
    fi
    

    but then subsequent redirections from file descriptor 3 will fail if the first redirection failed (after the condition was true).

    0 讨论(0)
  • 2020-12-13 14:43
    (
        if [ ...some condition here... ]; then
            exec <$fileName
        fi
        exec ./myscript
    )
    

    In a subshell, conditionally redirect stdin and exec the script.

    0 讨论(0)
  • 2020-12-13 14:43

    Standard input can also be represented by the special device file /dev/stdin, so using that as a filename will work.

    file="/dev/stdin"
    ./myscript < "$file"
    
    0 讨论(0)
提交回复
热议问题