Assign results of globbing to a variable in Bash

前端 未结 4 1647
孤独总比滥情好
孤独总比滥情好 2021-01-01 09:30

My colleague, Ryan, came to me with a bug in his Bash script, and I identified the problem with this test:

$ mkdir ryan
$ mkdir ryan/smells-bad
$ FOO=ryan/sm         


        
相关标签:
4条回答
  • 2021-01-01 09:38

    The problem is that the glob will only expand if the file "rotten_eggs" exists, because it is included in the glob pattern. You should use an array.

    FOO=( ryan/smells-* )
    touch "${FOO[@]/%//rotten_eggs}"
    

    The FOO array contains everything matched by the glob. The expansion using % appends /rotten_eggs to each element.

    0 讨论(0)
  • 2021-01-01 09:43

    Consider

    for dir in $FOO; do
        touch "$dir/rotten_eggs"
    done
    

    Note that this will touch multiple files if the glob pattern matches more than one pathname.

    0 讨论(0)
  • 2021-01-01 09:43

    I would do it like this:

    for FOO in ryan/smells-*; do
      touch "$FOO"/rotten_eggs
    done
    

    This way $FOO contains the actual directory name, not the glob pattern. If there's more than one match, though, it will only contain the last one after the loop, so the array solution might be better for that case.

    0 讨论(0)
  • 2021-01-01 09:59

    The code as intended with the result of the glob assigned to the variable would be like this:

    $ mkdir ryan
    $ mkdir ryan/smells-bad
    $ FOO=(ryan/smells-*)
    $ echo "${FOO[@]}"
    ryan/smells-bad
    $ echo "$FOO"
    ryan/smells-bad
    $ touch "$FOO/rotten_eggs"
    $ ls -l "$FOO"
    total 0
    -rw-r--r-- 1 ryan ryan 0 Mar  1 11:17 rotten_eggs
    

    $FOO is actually an array here, but $FOO also works to get the first element of the array.

    but, see how the glob can match more than one file (hence the array is a good idea)

    $ mkdir ryan/clean
    $ FOO=(ryan/*)
    $ echo "$FOO"
    ryan/clean
    $ echo "${FOO[@]}"
    ryan/clean ryan/smells-bad
    

    In these cases the results of the glob is assigned to the variable as desired, rather than the variable being expanded as a glob at point of use.

    Of course this means that the variable should really always be used in double quotation marks "..." otherwise if the filename itself (the glob expansion) also had a * in it, it would glob again.

    e.g.

    $ touch ryan/'*ea*'
    $ FOO=(ryan/*ea*)
    $ echo "${FOO[@]}"
    ryan/clean ryan/*ea*
    $ echo ${FOO[@]}
    ryan/clean ryan/clean ryan/*ea*
    
    0 讨论(0)
提交回复
热议问题