2012-02-10 5 views
22

Мой коллега, Райан, пришел ко мне с ошибкой в ​​его Баш скрипт, и я определил проблемы с этим испытанием:Присвоить результаты подстановка переменной в Bash

$ mkdir ryan 
$ mkdir ryan/smells-bad 
$ FOO=ryan/smells-* 
$ echo $FOO 
ryan/smells-bad 
$ touch $FOO/rotten_eggs 
touch: cannot touch `ryan/smells-*/rotten_eggs': No such file or directory 

Из этого я делаю вывод, что globbing происходит во время команды echo, а не при создании переменной FOO.

У нас есть несколько обходных путей, в порядке убывания ungracefulness:

touch `echo $FOO`/rotten_eggs 

Или:

pushd 
cd $FOO 
touch rotten_eggs 
popd 

Но ни один не удовлетворяет. Мне не хватает трюка?

ответ

29

Проблема в том, что glob будет расширяться только в том случае, если файл «rotten_eggs» существует, поскольку он включен в шаблон glob. Вы должны использовать массив.

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

В массиве FOO содержится все, что соответствует glob. Расширение с использованием% добавляет/rotten_eggs к каждому элементу.

+0

Спасибо, это объясняет это красиво. –

+0

Это совсем не проблема. Проблема заключается в том, что bash присваивает glob переменной перед расширением. Даже если файл существует, для переменной будет привязан глобус, а не имя файла; и что glob будет расширен при использовании –

+0

@SamLiddicott Пример OP работал бы, если бы существовал «rotten_eggs», поэтому проблема была более или менее. Время расширения шара не актуально в коротком примере OP. – jordanm

6

Рассмотрим

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

Обратите внимание, что это будет touch несколько файлов, если шаблон Глоб соответствует более чем один путь к файлу.

+0

Это будет работать, но для этого требуется, чтобы прикосновение выполнялось несколько раз, а не один раз. – jordanm

+1

Это, возможно, яснее, в зависимости от ситуации. –

3

Я хотел бы сделать это следующим образом:

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

Таким образом $FOO содержит фактическое имя каталога, а не шаблон Глоб. Если есть более одного совпадения, он будет содержать только последний после цикла, поэтому решение массива может быть лучше для этого случая.

0

Код по назначению с результатом Glob присвоенного переменной будет выглядеть так:

$ 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 на самом деле массив здесь, но $ FOO также работает, чтобы получить первый элемент массива ,

, но посмотрим, как Глоб может соответствовать более одного файла (следовательно, массив является хорошей идеей)

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

В этих случаях результаты Glob присваивается переменной по желанию, а не причем переменная расширяется как точка доступа в точке использования.

Конечно, это означает, что переменная должна всегда использоваться в двойных кавычках "...", иначе если имя файла (расширение glob) также было бы в нем *, оно снова заглохнет.

например.

$ touch ryan/'*ea*' 
$ FOO=(ryan/*ea*) 
$ echo "${FOO[@]}" 
ryan/clean ryan/*ea* 
$ echo ${FOO[@]} 
ryan/clean ryan/clean ryan/*ea* 
Смежные вопросы