Xargs разделяется на новые строки, а не пробелы

вот моя короткая проблема

$ echo 'for i in [email protected]; do echo arg: $i; done; echo DONE' > /tmp/test.sh
$ echo "ac\nbc\ncc\n" | xargs bash /tmp/test.sh 
arg: ac
arg: bc
arg: cc
DONE

Это то, что я ожидаю, но

$ echo "ac s\nbc s\ncc s\n" | xargs -d \n bash /tmp/test.sh
arg: ac
arg: s
arg: bc
arg: s
arg: cc
arg: s
DONE

Должен ли выход быть?

arg: ac s
arg: bc s
arg: cc s
DONE

Как получить второй вывод с помощью xargs?

Ответ 1

Try:

printf %b 'ac s\nbc s\ncc s\n' | xargs -d '\n' bash /tmp/test.sh

Вы пренебрегли цитатой \n, переданной в -d, что означает, что только n, а не \n был передан xargs в качестве разделителя - оболочки "\ (когда оболочка анализирует некорректную строку, \ функционирует как escape-символ, если обычный символ следует за \ - n в этом случае - используется только обычный символ).

Также посоветуйте @glenn jackman советовать удвоить котировку [email protected] внутри script (или вообще опустить часть in "[email protected]").

Также: xargs -d является расширением GNU, которое, например, не будет работать на FreeBSD/macOS. Чтобы он работал там, см. Решение @glenn jackman xargs -0.


Обратите внимание, что я использую printf, а не echo, чтобы убедиться, что экземпляры \n в строке интерпретируются как новые строки во всех оболочках, подобных Bourne:
В bash и ksh [1]2 → по умолчанию используется NOT интерпретация \ управляемых последовательностей (для этого вы должны использовать -e) - в отличие от zsh и строго POSIX-совместимые оболочки, такие как dash.
Поэтому printf является более переносимым выбором.

[1] Согласно руководству, ksh echo встроенный проявляет то же поведение, что и внешняя утилита echo основной платформы хоста; в то время как это может различаться на разных платформах, реализации Linux и BSD/OSX не интерпретируют escape-последовательности \ по умолчанию.

Ответ 2

В вашей оболочке script необходимо использовать "[email protected]" not [email protected]

См. http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters


Я вижу в руководстве xargs на моей машине:

xargs считывает элементы со стандартного ввода,        помечены пробелами [...] или символами новой строки

(акцент мой)

Таким образом:

$ echo $'ac s\nbc s\ncc s\n' | xargs bash /tmp/test.sh  
arg: ac
arg: s
arg: bc
arg: s
arg: cc
arg: s
DONE

$ printf "%s\0" "ac s" "bc s" "cc s" | xargs -0 bash /tmp/test.sh
arg: ac s
arg: bc s
arg: cc s
DONE

С первым вы получите эквивалент

bash /tmp/test.sh ac s bc s cc s

против использования нулевого разделителя

bash /tmp/test.sh "ac s" "bc s" "cc s"

Вам нужно четко указать, что разделитель с xargs, когда данные содержат пробелы.

$ printf "%s\n" "ac s" "bc s" "cc s" | xargs -d $'\n' bash /tmp/test.sh
arg: ac s
arg: bc s
arg: cc s
DONE

$ echo $'ac s\nbc s\ncc s\n' | xargs -d $'\n' bash /tmp/test.sh  
arg: ac s
arg: bc s
arg: cc s
arg:  
DONE

Обратите внимание на дополнительный аргумент arg в последнем случае, echo уже добавляет новую строку, поэтому вам не нужен дополнительный, если вы не используете echo -n