Правильное экранирование вывода из конвейера в xargs

С awk :

awk -F, '$3=="MP"&&$10=="S"&&$5!="MP"{a[$5]+=$12;b[$5]+=$15}
  END{for(i in a){print i","a[i]","b[i]}}' file

  • -F, устанавливает разделитель на , .
  • $ 3 == "MP" && $ 10 == "S" && $ 5! = "MP" {...} это условие из вопроса
    • a [5 долларов США] + = 12 долларов США; b [5 долларов США] + = 15 долларов США заполняет массив a общим значением 12 долларов США и массивом b ] на общую сумму 15 долларов .
  • END {...} этот блок запускается, когда awk завершает обработку всех строк.
    • for (i in a) ] проходит через a массив
    • print i »,« a [i] »,« b [i]} печатает значения в двух массивах и их индекс.

Результат:

AI,1,0.2
AIR,5,1
BIR,10,2
5
12.08.2017, 01:02
4 ответа

Спецификация POSIX дает вам пример для этого:

ls | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' | xargs -E '' printf '<%s>\n'

(с именами файлов, представляющими собой произвольные последовательности из байтов (, отличных от /и NULL )и sed/ xargs, ожидающих текста , вам также необходимо исправить локаль на C (, где все не -байты NUL будут действительными символами ), чтобы сделать это надежным (, за исключением xargsреализаций, которые имеют очень низкий предел максимальной длины аргумента ))

. ]

-E ''необходим для некоторых xargsреализаций, которые без него понимали бы аргумент _для обозначения конца ввода (, где echo a _ b | xargsвыводит aтолько для примера ).

С GNU xargsвы можете использовать:

ls | xargs -d '\n' printf '<%s>\n'

GNU xargsтакже имеет -0, который был скопирован несколькими другими реализациями, поэтому:

ls | tr '\n' '\0' | xargs -0 printf '<%s>\n'

немного более портативный.

Все они предполагают, что имена файлов не содержат символов новой строки. Если могут быть имена файлов с символами новой строки, вывод lsпросто не будет обрабатываться после -. Если вы получите:

a
b

Это могут быть либо два файла aи b, либо файл с именем a<newline>b, точно сказать невозможно.

GNU lsимеет --quoting-style=shell-always, который делает его вывод однозначным и может обрабатываться после -, но цитирование несовместимо с цитированием, ожидаемым xargs. xargsраспознают "...", \xи '...'формы цитирования. Но и "...", и '...'являются сильными кавычками и не могут содержать символы новой строки. (только \может экранировать символы новой строки для xargs),так что это несовместимо с цитированием sh, где только '...'являются сильными кавычками (и могут содержать символы новой строки ), но \<newline>является продолжением строки -(удаляется )вместо экранированной новой строки.

Вы можете использовать оболочку для разбора этого вывода, а затем вывести его в формате, ожидаемомxargs:

eval "files=($(ls --quoting-style=shell-always))"
[ "${#files[#]}" -eq 0 ] || printf '%s\0' "${files[@]}" |
  xargs -0 printf '<%s>\n'
8
27.01.2020, 20:33

Чрезвычайно глупо пытаться анализировать вывод команды ls , который не предназначен для анализадля передачи команды, которая не предназначена для работы с несколькими символами(например: новые строкии {}), когда оболочка делает это сама:

set -- *; for f; do echo "<$f>"; done

set    -- *
for    f
do     ls "$f"
done

Или в одной командной строке:

$ set -- *; for f; do echo "<$f>"; done
<name-with-backslash\>
<-name-with-dash-prefix>
<name-with-double-quote">
<name-with-single-quote'>
<name with space>
<safe-name>
<with_a
newline>

Обратите внимание, что вывод работает (и имеет n примеров в качестве последнего имени файла) с символами новой строки.

Или, если количество файлов делает оболочку медленной, используйте find:

$ find ./ -type f -exec echo '<{}>' \;
<./safe-name>
<./with_a
newline>
<./name-with-double-quote">
<./-name-with-dash-prefix>
<./name with space>
<./name-with-single-quote'>
<./name-with-backslash\>

Только учтите, что find обрабатывает все точечные файлы и все подкаталоги иначе, чем оболочка.

0
27.01.2020, 20:33

Чтобы xargsпонимал параметр ввода -0null -с разделителями, отправляющая сторона также должна применять нулевой разделитель к отправляемым данным.

В противном случае между ними нет синхронизации.

Одним из вариантов является команда GNU find, которая может размещать такие разделители:

find. -maxdepth 1 ! -name. -print0 | xargs -0 ls -ld
4
27.01.2020, 20:33

Как вы сказали, xargsне любит непарные двойные кавычки, если только вы не используете -0, но -0имеет смысл только в том случае, если вы подаете ему нулевые -завершающиеся данные. Итак, это не удается:

$ echo * | xargs
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
name-with-backslash -name-with-dash-prefix

Но это работает:

$ printf '%s\0' -- * | xargs -0
-- name-with-backslash\ -name-with-dash-prefix name-with-double-quote" name-with-single-quote' name with space safe-name

В любом случае, ваш базовый подход — не лучший способ сделать это. Вместо того, чтобы возиться с xargsи lsи еще чем-то, просто используйте шелл-глобы:

$ for f in *; do ls -l -- "$f"; done
-rw-r--r-- 1 terdon terdon 4142 Aug 11 16:03 a
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-backslash\'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 -name-with-dash-prefix
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name-with-double-quote"'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 "name-with-single-quote'"
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 'name with space'
-rw-r--r-- 1 terdon terdon 0 Aug 11 15:34 safe-name
3
27.01.2020, 20:33

Теги

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