Передача конвейера оценивает как параметр xargs для использования эхом оценки

Это перечислит весь PDFs:

$ find dir/ -name '*.pdf'
./dir/subdir2/subsubdir1/document.pdf
./dir/subdir3/another-document.pdf

Можно передать это по каналу к xargs получить его как разграниченную одиночным пробелом строку и канал это к tar создать архив:

$ find dir/ -name '*.pdf' | xargs tar czf dir.tar.gz

(Этот путь опускает пустые каталоги),

3
21.06.2011, 04:19
4 ответа

Во-первых, xargs не может работать здесь, потому что замены были бы выполнены в подпроцессе ( bash процессы это xargs запуски). Но только переменные среды передаются подпроцессам, не окружают переменные. Очевидно HOSTNAME уже был в среде Вашего сценария, когда она запустилась, но HOSTADDRESS не. В этой точке Вы могли бы испытать желание экспортировать все переменные, но даже затем xargs не хорошее решение, потому что оно страдает от многочисленного − проблем заключения в кавычки, если Ваш шаблон содержит \"' или если Вы хотите сохранить пробел, Вы - тост.

Теперь, смотря на Ваш текущий код: MESSAGE=`eval echo $TEMPLATE` сложный способ записать eval MESSAGE=$TEMPLATE, за исключением заключения в кавычки проблем. И у Вас действительно есть проблемы заключения в кавычки; например, Вы заметите, что весь Ваш пробел был свернут. Вы услышали о Bobby Tables, не так ли? Правила расширения Shell довольно сложны, но существуют некоторые правила, это сохранит Вас нормальными:

  • Всегда двойные кавычки вокруг подстановок переменных "$foo" и замены команды "$(bar)". Можно нарушить это правило, если Вы понимаете, почему необходимо опустить кавычки.
  • Использовать $(…) вместо `…` для замен команды. Заключение в кавычки в форме одинарной левой кавычки является тайным и не портативным, тогда как заключение в кавычки внутри $(…) работы обычно.
  • Использовать eval только если нажатый под прицелом. Если Вы делаете, очень осторожны, и делаете его максимально простым.

Таким образом, что может пойти не так, как надо с eval MESSAGE="$TEMPLATE"? Это заставляет оболочку оценить

MESSAGE=Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS

Ой, нам нужны кавычки вокруг части, которая должна быть значением MESSAGE. Кавычки должны добраться до eval, таким образом, они должны пойти буквально через первую стадию расширения оболочки: eval MESSAGE="\"$TEMPLATE\"".

MESSAGE="Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS"

Лучше, но теперь у Вас есть точно шаблон инжекции Bobby Tables — что, если шаблон содержит кавычку? Затем необходимо выйти из кавычек. Четыре символа, которые имеют особое значение между двойными кавычками, \"$`, и Вы хотите $ для сохранения его значения поэтому добавьте обратную косую черту перед другими тремя.

TEMPLATE=$(sed -e 's/[\\"`]/\\&/g' <template.txt)
eval MESSAGE="\"$TEMPLATE\""

Теперь оболочка оценит

MESSAGE="Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS
Name         : Bobby \"drop\" O'Tables"

и все хорошо.

Обратите внимание, что надлежащее заключение в кавычки здесь должно защитить от случайных проблем парсинга; кто бы ни управляет шаблоном, все еще имеет доступ оболочки (с $(hello)).

Если бы Вы хотели шаблон, включенный в Ваш сценарий оболочки, то это было бы естественно сделано с heredoc.

MESSAGE=$(cat <<EOF)
Hostname     : $HOSTNAME
Host Address : $HOSTADDRESS
EOF

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

2
27.01.2020, 21:32

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

Один подход должен был бы использовать здесь-документы удара (все команды, показанные, как введено при приглашении оболочки):

  1. создайте шаблонный файл:

    $ cat template.txt
    Hi, $NAME
    
    Welcome to $PLACE
    
  2. экспортируйте переменные, которыми нужно заменить в тексте шаблона:

    $ export NAME=Bob
    $ export PLACE='unix&linux'
    
  3. создайте переменную оболочки, которая содержит единственную "новую строку" \n символ; в ударе самый легкий путь состоит в том, чтобы просто открыть строку, ввести новую строку и закрыть его:

    $ newline='
    > '
    
  4. наконец, назовите удар, чтобы сделать замену:

    $ bash -c "cat <<__EOF__${newline}$(cat template.txt)${newline}__EOF__"
    Hi, Bob
    
    Welcome to unix&linux
    

Почему это работает? Давайте вскроем противоречия в нем от нижней части: Вы просите колотить для выполнения команды ( -c опция), но подстановка переменных ${newline} и расширение команды $(cat ...) произойдите в родительской оболочке прежде bash -c ... на самом деле выполняется. Таким образом, результат - это bash -c видит командную строку, которая встраивает новые строки и целый текст шаблона. Это - как будто Вы ввели следующее при подсказке удара:

cat <<__EOF__
...contents of template.txt...
__EOF__

Также обратите внимание, что подставляемые переменные должны быть экспортированы, так как переменные присвоены в родительской оболочке, но это - "дочерний" удар, который делает замену.

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

2
27.01.2020, 21:32

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

export HOSTNAME HOSTADDRESS
(
NL='
'
DELIM=$'\177'
esc="'\''"
export IFS=""
HOSTNAME="SH_SQL_00'8'9"
HOSTADDRESS='172.16.3.44 Bobby "drop" O'\''Tables ${PWD} $(ls)'
printf '%s\n' 'Hostname : $HOSTNAME' 'Host Address : $HOSTADDRESS' > template.txt
TEMPLATE="$(<template.txt)"
TEMPLATE="${TEMPLATE//\'/${esc}}"       # escape every single quote: ' --> '\'' (which later will become ''\''' by '${TEMPLATE}')
TEMPLATE="${TEMPLATE//${NL}/${DELIM}}"  # replace every newline char with a delim char
evalstr="echo '${TEMPLATE}'"
#printf '\n%s\n\n' "${evalstr}" | LC_ALL=C vis -fotc
set -xv
eval eval "${evalstr}"
) | LC_ALL=C tr '\177' '\n' | nl
0
27.01.2020, 21:32
MESSAGE=$(eval $(cat template.txt))
-3
27.01.2020, 21:32
  • 1
    я думаю, что Вы имели в виду $(eval echo $(cat ... - но это свернуло бы текст в одну строку. –  Riccardo Murri 21.06.2011, 11:55
  • 2
    я не уверен, что Вы имели в виду, но это не собирается помогать Kev написать рабочий код. –  Gilles 'SO- stop being evil' 21.06.2011, 12:00
  • 3
    Gilles: Я думаю, что мое решение работает с эхом, упомянутым Riccardo, и я думаю, что это был ответ для его вопроса... Так или иначе она/он уже получила решение почта :) –  MSpike 22.06.2011, 09:49

Теги

Похожие вопросы