2016-09-20 4 views
0

Say У меня есть переменная $ARGS, которая содержит следующее:BASH оболочки расширить аргументы с пробелами с переменной

file1.txt "second file.txt" file3.txt 

Как я могу передать содержимое $ARGS в качестве аргументов команды (скажем cat $ARGS, например), обрабатывая "second file.txt" как один аргумент, а не разбивая его на "second и file.txt"?

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

Спасибо!

+6

Не используйте переменную; используйте массив: 'ARGS = (" file1.txt "" второй файл.txt "" file3.txt ")', а затем передайте его: '" $ {ARGS [@]} "'. –

+0

Если параметры находятся в текстовом файле, а разделитель между аргументами - это пробел, я не понимаю, почему вы считаете поведение, которое вы считаете неправильным. Я имею в виду, что я могу видеть, что вы хотели бы сделать, но тогда чтение аргументов, разделенных пробелами, из текстового файла, вероятно, является неправильным решением. Возможно, вы используете какой-то структурированный формат файла, если ваши потребности сложны. – tripleee

ответ

2

Это можно сделать либо без Баш массивов или eval: Это один из несколько мест, где поведение xargs без -0 или -d расширений (поведение, которое в основном создает ошибки) действительно полезно.

# this will print each argument on a different line 
# ...note that it breaks with arguments containing literal newlines! 
xargs printf '%s\n' <<<"$ARGS" 

... или ...

# this will emit arguments in a NUL-delimited stream 
xargs printf '%s\0' <<<"$ARGS" 

# in bash 4.4, you can read this into an array like so: 
readarray -t -d '' args < <(printf '%s\0' "$ARGS") 
yourprog "${args[@]}" # actually run your programs 

# in bash 3.x or newer, it's just a bit longer: 
args=(); 
while IFS= read -r -d '' arg; do 
    args+=("$args") 
done < <(xargs printf '%s\0' <<<"$ARGS") 
yourprog "${args[@]}" # actually run your program 

# in POSIX sh, you can't safely handle arguments with literal newlines 
# ...but, barring that, can do it like this: 
set -- 
while IFS= read -r arg; do 
    set -- "[email protected]" "$arg" 
done < <(printf '%s\n' "$ARGS" | xargs printf '%s\n') 
yourprog "[email protected]" # actually run your program 

... или, позволяя xargs сам сделать вызов:

# this will call yourprog with ARGS given 
# ...but -- beware! -- will cause bugs if there are more arguments than will fit on one 
# ...command line invocation. 
printf '%s\n' "$ARGS" | xargs yourprog 
1

Как упоминалось Джонатаном Леффлером, вы можете сделать это с помощью массива.

my_array=("file1.txt" "second file.txt" "file3.txt") 
cat "${my_array[1]}" 

индексировать массива начинается с 0. Таким образом, если вы хотите cat первый файл в вашем массиве вы бы использовать индексный номер 0. "${my_array[0]}". Если вы хотите запустить свою команду для всех элементов, замените номер индекса на @ или *. Например, вместо "${my_arryay[0]}" вы должны использовать "${my_array[@]}" Убедитесь, что вы приводите массив или обрабатываете любое имя файла пробелами в виде отдельных файлов.

Альтернативно, если по какой-либо причине цитирование массива является проблемой, вы можете установить IFS (который обозначает внутренний разделитель полей), чтобы он соответствовал новой строке. Если вы это сделаете, рекомендуется сохранить IFS по умолчанию в переменную перед ее изменением, чтобы вы могли вернуть ее так, как только она завершилась. Например:

# save IFS to a variable  
old_IFS=${IFS-$' \t\n'} 
#set IFS to a newline 
IFS='$\n' 

# run your script 
my_array=("file1.txt" "second file.txt" "file3.txt") 
cat ${my_array[1]} 

# restore IFS to its default state 
IFS=$old_IFS 

Возможно, лучше не вмешиваться в IFS, если вам не нужно. Если вы можете процитировать массив, чтобы заставить ваш скрипт работать, тогда вы должны это сделать.

Для гораздо более глубокого взгляда на использование массивов см:

+0

Пожалуйста, подумайте об использовании источника, отличного от АБС, - это [довольно печально известный] (http://wooledge.org/~greybot/meta/abs) для использования плохих методов в его примерах. Там [страница bash-hackers на массивах] (http://wiki.bash-hackers.org/syntax/arrays), [BashGuide на массивах] (http://mywiki.wooledge.org/BashGuide/Arrays), и [BashFAQ # 5] (http://mywiki.wooledge.org/BashFAQ/005). –

+0

Кроме того, если ваше исходное значение IFS не задано, перемещение 'old_IFS', которое вы здесь делаете, не будет вести себя так, как вы планируете. Рассмотрим «old_IFS = $ {IFS- $ '\ t \ n'}', чтобы сохранить значение по умолчанию в 'old_IFS', если исходное значение не установлено (в отличие от set-to-a-empty-string). –

+0

Спасибо за информацию. Я обновил ответ. –

0

Без bashизмов, код обычной оболочки может понадобиться eval :

# make three temp files and list them. 
cd /tmp ; echo ho > ho ; echo ho ho > "ho ho" ; echo ha > ha ; 
A='ho "ho ho" ha' ; eval grep -n '.' $A 

Выход:

ho:1:ho 
ho ho:1:ho ho 
ha:1:ha 

Обратите внимание, что eval является мощным и if not used responsibly can lead to mischief ...