/dev/stdin with herestring

前端 未结 4 1260
日久生厌
日久生厌 2020-12-18 03:19

I would like a Bash script that can take input from a file or stdin, much like grep, for example

$ cat          


        
相关标签:
4条回答
  • 2020-12-18 03:24

    bash parses some file names (like /dev/stdin) specially, so that they are recognized even if they are not actually present in the file system. If your script doesn't have #!/bin/bash at the top, and /dev/stdin isn't in your file system, your script may be run using /bin/sh, which would expect /dev/stdin to actually be a file.

    (This should, perhaps, not be an answer, but rather a comment to Austin's answer.)

    0 讨论(0)
  • 2020-12-18 03:33

    You got a typo here

    read b < "${1-/dev/stdin}"
    

    Try

    read b < "${1:-/dev/stdin}"
    
    0 讨论(0)
  • 2020-12-18 03:42

    Using /dev/stdin in this manner can be problematic because you are attempting to get a handle to stdin using a name in the filesystem (/dev/stdin) rather than using the file descriptor which bash has already handed you as stdin (file descriptor 0).

    Here's a small script for you to test:

    #!/bin/bash
    
    echo "INFO: Listing of /dev"
    ls -al /dev/stdin
    
    echo "INFO: Listing of /proc/self/fd"
    ls -al /proc/self/fd
    
    echo "INFO: Contents of /tmp/sh-thd*"
    cat /tmp/sh-thd*
    
    read b < "${1-/dev/stdin}"
    echo "b: $b"
    

    On my cygwin installation this produces the following:

    ./s <<< 'Hello world'
    
    
    $ ./s <<< 'Hello world'
    INFO: Listing of /dev
    lrwxrwxrwx 1 austin None 15 Jan 23  2012 /dev/stdin -> /proc/self/fd/0
    INFO: Listing of /proc/self/fd
    total 0
    dr-xr-xr-x 2 austin None 0 Mar 11 14:27 .
    dr-xr-xr-x 3 austin None 0 Mar 11 14:27 ..
    lrwxrwxrwx 1 austin None 0 Mar 11 14:27 0 -> /tmp/sh-thd-1362969584
    lrwxrwxrwx 1 austin None 0 Mar 11 14:27 1 -> /dev/tty0
    lrwxrwxrwx 1 austin None 0 Mar 11 14:27 2 -> /dev/tty0
    lrwxrwxrwx 1 austin None 0 Mar 11 14:27 3 -> /proc/5736/fd
    INFO: Contents of /tmp/sh-thd*
    cat: /tmp/sh-thd*: No such file or directory
    ./s: line 12: /dev/stdin: No such file or directory
    b: 
    

    What this output shows is that bash is creating a temporary file to hold your HERE document (/tmp/sh-thd-1362969584) and making it available on file descriptor 0, stdin. However, the temporary file has already been unlinked from the file system and so is not accessible by reference through a file system name such as /dev/stdin. You can get the contents by reading file descriptor 0, but not by trying to open /dev/stdin.

    On Linux, the ./s script above gives the following, showing that the file has been unlinked:

    INFO: Listing of /dev
    lrwxrwxrwx 1 root root 15 Mar 11 09:26 /dev/stdin -> /proc/self/fd/0
    INFO: Listing of /proc/self/fd
    total 0
    dr-x------ 2 austin austin  0 Mar 11 14:30 .
    dr-xr-xr-x 7 austin austin  0 Mar 11 14:30 ..
    lr-x------ 1 austin austin 64 Mar 11 14:30 0 -> /tmp/sh-thd-1362965400 (deleted) <---- /dev/stdin not found
    lrwx------ 1 austin austin 64 Mar 11 14:30 1 -> /dev/pts/12
    lrwx------ 1 austin austin 64 Mar 11 14:30 2 -> /dev/pts/12
    lr-x------ 1 austin austin 64 Mar 11 14:30 3 -> /proc/10659/fd
    INFO: Contents of /tmp/sh-thd*
    cat: /tmp/sh-thd*: No such file or directory
    b: Hello world
    

    Change your script to use the stdin supplied, rather than trying to reference through /dev/stdin.

    if [ -n "$1" ]; then
        read b < "$1"
    else
        read b
    fi
    
    0 讨论(0)
  • 2020-12-18 03:43
    $ cat ts.sh 
    read b < "${1-/dev/stdin}"
    echo $b
    
    $ ./ts.sh <<< 'hello world'
    hello world
    

    No problem for me. I'm using bash 4.2.42 on Mac OS X.

    0 讨论(0)
提交回复
热议问题