Я хотел бы запустить команду оболочки для каждой строки, взятой из STDIN.
В этом случае я хотел бы запустить xargs mv
. Например, даны две строки:
mfoo foo
mbar bar
Я хотел бы баллотироваться:
xargs mv mfoo foo
xargs mv mbar bar
Я пробовал следующие стратегии с ruby
, awk
, и xargs
. Однако я делаю это неправильно:
Только xargs
:
$ echo "mbar bar\nmbaz baz" | xargs mv
usage: mv [-f | -i | -n] [-v] source target
mv [-f | -i | -n] [-v] source ... directory
Через awk
:
$ echo "mbar bar\nmbaz baz" | awk '{ system("xargs $0") }'
Через ruby
:
$ echo "mbar bar\nmbaz baz" | ruby -ne '`xargs mv`'
$ ls
cat foo mbar mbaz
У меня есть несколько вопросов:
- Как мне сделать то, что я пытаюсь сделать?
- Что не так с каждой из моих попыток?
- Есть ли лучший способ «подумать» о том, что я пытаюсь сделать?
Меня особенно смущает то, что моя xargs
попытка не срабатывает, поскольку следующее работает:
$ echo "foo\nbar" | xargs touch
$ ls
bar foo
решение1
echo "mbar bar\nmbaz baz" | xargs mv
С xargs
вы должны использовать -t
опцию, чтобы увидеть, что происходит. Так что в вашем случае выше, если мы вызовем xargs
с -t
, что мы увидим:
mv mbar bar mbaz baz
Так что, очевидно, это не правильно. Произошло то, что, xargs
как голодный крокодил, съел все аргументы, которые ему передали через канал echo
. Поэтому вам нужен способ ограничить передачу аргументов кроку. И поскольку вы запросили построчно, то вам нужна опция -l
или -L
для POSIX.
echo "mbar bar\nmbaz baz" | xargs -l -t mv
Способ в стиле POSIX:
echo "mbar bar\nmbaz baz" | xargs -L 1 -t mv
mv mbar bar
mv mbaz baz
И это то, что вы хотели. HTH
решение2
Вы можете пойти следующим путем:
echo -e "foo bar\ndoo dar" | xargs -n 2 mv
Чтобы прочитать из файла:
cat lines.txt | xargs -n 2 mv
или
xargs -a lines.txt -n 2 mv
решение3
xargs
может вести себя не так, как вы ожидаете. См. комментарий @MichaelHomer к этому вопросу:
xargs mv mfoo foo
будет ждать ввода и запускать mvmfoo foo $x_1 $x_2 $x_3...
для каждой$x_n
полученной строки ввода. @MichaelHomer
В этом можно убедиться, прочитавxargs
страница руководства:
ОПИСАНИЕ
Утилита xargs считывает строки, разделенные пробелами, табуляциями, символами новой строки и конца файла, из стандартного ввода и запускает утилиту со строками в качестве аргументов.
На практике, похоже, что xargs
он будет игнорировать новые строки при подаче аргументов утилите командной строки, заданных в качестве ее первого аргумента. Например, обратите внимание, что происходит с новой строкой:
$ echo "foo bar\ncat dog"
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs echo
foo bar cat dog
Это поведение можно контролировать с помощью-n
флаг:
-n число
Установите максимальное количество аргументов, принимаемых из стандартного ввода для
каждого вызова утилиты.
Возвращаясь к предыдущему примеру, если мы хотим xargs
взять только 2 аргумента и выйти, то мы можем использоватьПодход @ddnomad:
$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog
Также, как показано в комментариях к ответу Ракеша Шармы, на BSD xargs
вы можете указать количество строк для чтения с помощью -L
флага. Из страницы руководства:
-L номер
Вызов утилиты для каждого числа прочитанных строк. Если достигнут EOF и
прочитано меньше строк, чем число, то утилита будет вызвана с доступными строками.
Не менее полезным для тестирования является -t
переключатель:
Возвращаясь к нашему примеру, оба следующих варианта будут работать; однако второй пример лучше, поскольку именно это и имелось в виду в данном случае:
$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs -L 1 echo
foo bar
cat dog
Примечание со страницы руководства:
Параметры -L и -n являются взаимоисключающими; будет использоваться последний указанный параметр.
решение4
Есть очень простое решение, которое не использует xargs.
Определим эту функцию:
$ mv2() { while (($# >1 )); do echo mv "$1" "$2"; shift 2; done; }
Затем просто вызовите его со списком необходимых имен файлов (переводы строк не требуются):
$ mv2 mbar bar mbaz baz
mv mbar bar
mv mbaz baz
Уберите эхо, чтобы функция действительно заработала.