Заставьте xargs выполнять команду один раз для каждой строки ввода

StackOverflow https://stackoverflow.com/questions/199266

  •  11-07-2019
  •  | 
  •  

Вопрос

Как я могу заставить xargs выполнять команду ровно один раз для каждой введенной строки?Поведение по умолчанию заключается в разделении строк на части и выполнении команды один раз, передавая несколько строк в каждый экземпляр.

От http://en.wikipedia.org/wiki/Xargs:

поиск /путь -введите f -print0 | xargs -0 rm

В этом примере find передает входные данные xargs с длинным списком имен файлов.затем xargs разбивает этот список на подсписки и вызывает rm один раз для каждого подсписка.Это более эффективно, чем эта функционально эквивалентная версия:

найти /ввести путь f -exec rm '{}' \;

Я знаю, что find имеет флаг "exec".Я просто цитирую наглядный пример из другого ресурса.

Это было полезно?

Решение

Следующее будет работать только в том случае, если у вас нет пробелов во входных данных:

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

со справочной страницы:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

Другие советы

Мне кажется, что все существующие ответы на этой странице неверны, включая ответ, помеченный как правильный. Это связано с тем, что вопрос сформулирован неоднозначно.

Резюме . & nbsp; Если вы хотите выполнить команду " ровно один раз для каждой указанной строки ввода, " передав всю команду (без новой строки) в качестве одиночного аргумента, тогда это лучший UNIX-совместимый способ сделать это:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargs может иметь или не иметь полезных расширений, которые позволяют вам отказаться от tr , но они недоступны в OS X и других системах UNIX.

Теперь для длинного объяснения ...

<Ч>

При использовании xargs необходимо учитывать две проблемы:

<Ол>
  • как он разделяет входные данные на "аргументы"; и
  • сколько аргументов для передачи дочерней команды за раз.
  • Чтобы протестировать поведение xargs, нам нужна утилита, которая показывает, сколько раз он выполняется и сколько аргументов. Я не знаю, есть ли стандартная утилита для этого, но мы можем довольно легко ее кодировать в bash:

    #!/bin/bash
    echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
    

    Если вы сохраните его как show в своем текущем каталоге и сделаете его исполняемым, вот как это работает:

    $ ./show one two 'three and four'
    -> "one" "two" "three and four" 
    

    Теперь, если исходный вопрос действительно касается пункта 2. выше (как мне кажется, после прочтения его несколько раз), и его следует читать так (изменения выделены жирным шрифтом):

      

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

    тогда ответ - -n 1 .

    Давайте сравним поведение по умолчанию для xargs, которое разделяет вводные данные вокруг пробела и вызывает команду как можно меньше раз:

    $ echo one two 'three and four' | xargs ./show 
    -> "one" "two" "three" "and" "four" 
    

    и его поведение с помощью -n 1 :

    $ echo one two 'three and four' | xargs -n 1 ./show 
    -> "one" 
    -> "two" 
    -> "three" 
    -> "and" 
    -> "four" 
    

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

      

    Как я могу заставить xargs выполнять команду с точно одним аргументом для каждой заданной строки ввода? Поведение по умолчанию состоит в разбиении строк вокруг пробела .

    тогда ответ более тонкий.

    Можно подумать, что -L 1 может помочь, но оказывается, что он не меняет парсинг аргументов. Он выполняет команду только один раз для каждой строки ввода с таким количеством аргументов, сколько было в этой строке ввода:

    $ echo 
    
    

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    $ echo 
    
    

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    $ echo 
    
    

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    $ echo one\ntwo\nthree and four' | xargs -L 1 ./show 
    -> "one" 
    -> "two" 
    -> "three" "and" "four" 
    

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show -> "one " "two" "three and four"

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show -> "one " -> "two" -> "three and four" one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show -> "one " "two" "three and four"

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

    Мало того, но если строка заканчивается пробелом, она добавляется к следующему:

    <*>

    Очевидно, что -L не касается изменения способа, которым xargs разделяет входные данные на аргументы.

    Единственный аргумент, который делает это кроссплатформенным способом (исключая расширения GNU), это -0 , который разбивает входные данные вокруг байтов NUL.

    Тогда нужно просто переводить строки в NUL с помощью tr :

    <*>

    Теперь разбор аргументов выглядит хорошо, включая завершающий пробел.

    Наконец, если вы объедините эту технику с -n 1 , вы получите ровно одно выполнение команды на каждую строку ввода, независимо от того, какой у вас ввод, что может быть еще одним способом взглянуть на исходный вопрос ( возможно, наиболее интуитивный, учитывая название):

    <*>

    Если вы хотите запустить команду для каждый линия (т.е.результат), исходящий от find, тогда для чего вам нужен xargs для чего?

    Попробуй:

    find путь -type f -exec ваше-командование {} \;

    где буквальный {} заменяется именем файла и литералом \; необходим для find знать, что пользовательская команда заканчивается на этом.

    Редактировать:

    (после редактирования вашего вопроса, уточняющего, что вы знаете о -exec)

    От man xargs:

    -L максимальное количество линий
    Используйте максимум максимальное количество линий непустые строки ввода для каждой командной строки.Завершающий пробелы приводят к логическому продолжению строки ввода на следующей строке ввода.Подразумевает -x.

    Обратите внимание, что имена файлов, заканчивающиеся пробелами, могут вызвать у вас проблемы, если вы используете xargs:

    $ mkdir /tmp/bax; cd /tmp/bax
    $ touch a\  b c\  c
    $ find . -type f -print | xargs -L1 wc -l
    0 ./c
    0 ./c
    0 total
    0 ./b
    wc: ./a: No such file or directory
    

    Так что, если вас не волнует -exec вариант, который вам лучше использовать -print0 и -0:

    $ find . -type f -print0 | xargs -0L1 wc -l
    0 ./c
    0 ./c
    0 ./b
    0 ./a
    
      

    Как я могу заставить xargs выполнять команду ровно один раз для каждой заданной строки ввода?

    Я не использую ответ -L 1 , потому что он не обрабатывает файлы с пробелами, что является ключевой функцией find -print0 .

    echo "file with space.txt" | xargs -L 1 ls
    ls: file: No such file or directory
    ls: space.txt: No such file or directory
    ls: with: No such file or directory
    

    Лучшее решение - использовать tr для преобразования новых строк в нулевые ( \ 0 ) символы, а затем использовать аргумент xargs -0 .

    echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
    file with space.txt
    

    Если вам необходимо ограничить количество вызовов, вы можете использовать аргумент -n 1 , чтобы сделать один вызов программы для каждого ввода:

    echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
    

    Это также позволяет вам фильтровать вывод команды find перед преобразованием разрывов в нули.

    find . -name \*.xml | grep -v /workspace/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
    

    Еще одна альтернатива ...

    find /path -type f | while read ln; do echo "processing $ln"; done
    
    find path -type f | xargs -L1 command 
    

    это все, что вам нужно.

    Эти два способа также работают и будут работать для других команд, которые не используют find!

    xargs -I '{}' rm '{}'
    xargs -i rm '{}'
    

    пример использования:

    find . -name "*.pyc" | xargs -i rm '{}
    

    удалит все файлы pyc в этом каталоге, даже если файлы pyc содержат пробелы.

    Следующая команда найдет все файлы (-type f) в / path , а затем скопирует их, используя cp , в текущую папку. Обратите внимание на использование if -I% для указания символа-заполнителя в командной строке cp , чтобы аргументы можно было размещать после имени файла.

    find / path -type f -print0 | xargs -0 -I% cp%.

    Протестировано с xargs (GNU findutils) 4.4.0

    Вы можете ограничить количество строк или аргументов (если между аргументами есть пробелы), используя флаги --max-lines или --max-args соответственно.

      -L max-lines
             Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
             line.  Implies -x.
    
      --max-lines[=max-lines], -l[max-lines]
             Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
             is deprecated since the POSIX standard specifies -L instead.
    
      --max-args=max-args, -n max-args
             Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
             unless the -x option is given, in which case xargs will exit.
    

    Кажется, у меня недостаточно репутации, чтобы добавить комментарий к ответу Тобиа выше , поэтому я добавляю это & Quot; ответ & Quot; чтобы помочь тем из нас, кто хочет поэкспериментировать с xargs на платформах Windows.

    Вот пакетный файл Windows, который делает то же самое, что и быстро закодированный "show", показанный Tobia. Сценарий:

    @echo off
    REM
    REM  cool trick of using "set" to echo without new line
    REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
    REM
    if "%~1" == "" (
        exit /b
    )
    
    <nul set /p=Args:  "%~1"
    shift
    
    :start
    if not "%~1" == "" (
        <nul set /p=, "%~1"
        shift
        goto start
    )
    echo.
    

    @Draemon отвечает правильно с " -0 " даже с пробелом в файле.

    Я пробовал команду xargs и обнаружил, что " -0 " отлично работает с < -L " ;. обрабатываются даже пробелы (если ввод был завершен нулем). Ниже приведен пример:

    #touch "file with space"
    #touch "file1"
    #touch "file2"
    

    Следующее разделит пустые значения и выполнит команду для каждого аргумента в списке:

     #find . -name 'file*' -print0 | xargs -0 -L1
    ./file with space
    ./file1
    ./file2
    

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

     #find . -name 'file*' -print0 | xargs -0 | xargs -L1
     ./file with space ./file1 ./file2
    

    даже это будет выполнено один раз:

     #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
    ./file with space ./file1 ./file2
    

    Команда будет выполнена один раз как " -L " теперь не разделяется на нулевой байт. вам нужно указать оба параметра "-0" и < -L " работать.

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

    выполните очистку муравьиной задачи для каждого файла build.xml в текущей или вложенной папке.

    find . -name 'build.xml' -exec ant -f {} clean-all \;
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top