От новичка до гуру: Курсы программирования на CyberDuff

Оболочка BASH расширяет аргументы пробелами из переменной

Скажем, у меня есть переменная $ARGS, которая содержит следующее:

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

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

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

Спасибо!


  • Не используйте переменную; используйте массив: ARGS=( "file1.txt" "second file.txt" "file3.txt" ), а затем передайте его: "${ARGS[@]}". 20.09.2016
  • Если настройки находятся в текстовом файле, а разделителем между аргументами является пробел, я не понимаю, почему вы считаете поведение, которое видите, неправильным. Я имею в виду, что я вижу, что вы хотели бы сделать, но тогда чтение аргументов, разделенных пробелами, из текстового файла, вероятно, является неправильным решением. Возможно, используйте какой-то структурированный формат файла, если ваши потребности сложны. 20.09.2016

Ответы:


1

Это можно сделать без массивов bash или 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"

...or...

# 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 < <(xargs 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+=( "$arg" )
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 -- "$@" "$arg"
done < <(printf '%s\n' "$ARGS" | xargs printf '%s\n')
yourprog "$@" # 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
20.09.2016

2

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

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, если в этом нет необходимости. Если вы можете процитировать массив, чтобы ваш скрипт работал, вы должны это сделать.

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

20.09.2016
  • Пожалуйста, рассмотрите возможность использования источника, отличного от ABS — он довольно печально известен использованием неправильных практик в его примеры. Есть страница bash-hackers по массивам, BashGuide по массивам и Часто задаваемые вопросы по Bash #5. 20.09.2016
  • Кроме того, если ваше исходное значение IFS не установлено, тасовка old_IFS, которую вы здесь делаете, не будет вести себя так, как вы предполагали. Рассмотрим old_IFS=${IFS-$' \t\n'}, чтобы сохранить значение по умолчанию в old_IFS, если исходное значение не установлено (в отличие от установки в пустую строку). 20.09.2016
  • Спасибо за информацию. Я обновил ответ. 21.09.2016
  • Кстати, IFS='$\n' сильно отличается от IFS=$'\n'. В первом случае ваш IFS состоит из трех символов, $, обратной косой черты и n. В последнем это один символ, буквальный перевод строки. 15.03.2017
  • Я наткнулся здесь, нашел этот ответ и смог решить давнюю ошибку. Почему множество других ответов SO, которые изменяют IFS, не предупреждают о восстановлении, сбивает с толку. Благодарю вас! 30.11.2019

  • 3

    Без bashisms простому коду оболочки может понадобиться 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 является мощным и если не использовать ответственно, может привести к вреду...

    20.09.2016
    Новые материалы

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..

    Игра в прятки с данными
    Игра в прятки с данными Я хотел бы, чтобы вы сделали мне одолжение и ответили на следующие вопросы. Гуглить можно в любое время, здесь никто не забивается. Сколько регионов в Гане? А как..

    «Раскрытие математических рассуждений с помощью Microsoft MathPrompter и моделей больших языков»
    TL;DR: MathPrompter от Microsoft показывает, как использовать математические рассуждения с большими языковыми моделями; 4-этапный процесс для улучшения доверия и рассуждений в математических..

    Раскройте свой потенциал в области разработки мобильных приложений: Абсолютная бесплатная серия
    Глава 6: Работа в сети и выборка данных Глава 1: Введение в React Native Глава 2: Основы React Native Глава 3: Создание пользовательского интерфейса с помощью React Native Глава 4:..

    Все о кейсах: Camel, Snake, Kebab & Pascal
    В программировании вы сталкивались с ними при именовании переменной, класса или функции. Поддержание согласованности типов и стилей случаев делает ваш код более читабельным и облегчает совместную..

    Как работает дистанционное парное программирование и почему оно может изменить вашу жизнь
    Серебряная пуля от одиночества и отвлекающих факторов во время обучения программированию Независимо от того, работаете ли вы или учитесь удаленно, велика вероятность, что одиночество и..