Intereting Posts
Как предоставить пользователям или группе разрешение linux sudo с ограниченным разрешением на определенные файлы Не удалось выделить запрошенные разделы при установке Scientific Linux Когда мы используем команду mv, какие изменения происходят в HDD? копировать массив с именем массива внутри строки в bash Будет ли всегда пара одинаковых UID и GID? zsh: проблемы с переименованием файлов с zmv Что означает @ перед именем каталога? Запуск программы, скомпилированной с помощью gcc Журнал показывает очень старые ботинки, которые не перерабатываются Отключение сочетания клавиш для изменения настройки отображения в gnome-shell Чтение файла с циклом IFS работает только тогда, когда не используются массивы Как получить доступ к установке ubuntu wubi Сетевое соединение Ubuntu 10.10 сократилось через несколько минут после запуска SELinux: chcon для типа, ls -Z показывает еще один «Преобразование» файловой системы из ext3 в ext4

Найдите полный путь и имя файла под каталогом, затем перейдите в исполняемый файл в качестве аргументов

Я хотел бы найти полный путь и имя файла всего .txt под каталогом и перейти к исполняемому файлу ./thulac .

Это стоило мне времени:

 find /mnt/test -name "*.txt" -print0 |xargs -l bash -c './thulac < $0' 

Но это только находит полный путь.

Из xargs с несколькими аргументами я вижу:

 echo argument1 argument2 argument3 | \ xargs -l bash -c 'echo this is first:$0 second:$1 third:$2' | xargs 

Я хочу добиться чего-то вроде:

 find /mnt/test -name "*.txt" -print0 -printf "%f" | \ xargs -0 bash -c './thulac < $0 > $1' 

Хотя здесь, xargs не могут правильно разделить -print0 -printf "%f" как два аргумента, когда есть несколько файлов, которые застряли у меня.


Пример:

 find /mnt/test -name "*.txt" -print0 -printf "%f" | \ xargs -0 -I bash -c './thulac < $0 > /mnt/tokenized/$1' 
  1. Если /mnt/test имеет только один файл, указанная выше команда работает.

  2. Но если /mnt/test имеет более одного файла (независимо от языка):

     [root@localhost THULAC]# ls /mnt/test test33.txt test.txt [root@localhost THULAC]# find /mnt/test -name "*.txt" -print0 -printf "%f" | \ xargs -0 bash -c './thulac < $0 > /mnt/tokenized/$1' /mnt/test/test.txt: /mnt/tokenized/test.txt/mnt/test/test33.txt: No such file or directory 

    Как вы видите, xargs смешивает два пути вместе /mnt/tokenized/test.txt/mnt/test/test33.txt , что приводит к ошибке No such file or directory .

Как заставить его работать?

 find /tmp/test -name '*.txt' \ -exec bash -c './thulac < "$(readlink -f {})" > "/mnt/tokenized/$(basename {})"' \; 

Используйте поиск для поиска файлов и выполнения команд по результатам. С помощью команды bash -c 'command' вы можете выполнить несколько $ ().

Используйте readlink -f {} чтобы создать полный путь к результату.

Используйте basename {} чтобы удалить путь из результата.

При работе с xargs вы всегда должны проверять свои решения с помощью ввода, начинающегося с «-» и содержащего двойное пространство »и« потому что xargs является печально известным для плохого использования:

 mkdir -- '-" '"'" seq 10 > ./-\"\ \ \'/'-" '"'".txt 

Вот решение, использующее GNU Parallel:

 find . -name "*.txt" -print0 |parallel -0 ./thulac '<' {} '>' {/} 

<&> Нужно указывать, поскольку в противном случае они будут интерпретироваться оболочкой, которая начинается parallel . Мы хотим, чтобы они вместо этого интерпретировались оболочкой, запущенной parallel .

 find /mnt/test -name "*.txt" -print0 -printf "%f\0" | xargs -0 -n 2 bash -c 'shift $1; ./thulac < $1 > /mnt/tokenized/$2' 2 1 

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

В противном случае произойдет то, что полное имя одного файла будет объединено в базовое имя следующего файла – явление, которое вы наблюдали в случае нескольких имен файлов!

И тогда вам нужно подавать 2 аргумента за раз в bash alligator , иначе он будет потреблять столько, сколько ему разрешено, но он передает только первые два в ваш исполняемый файл ./thulac .

Лучшая альтернатива заключается в том, чтобы обойтись без xargs и выполнять всю свою работу в find , поскольку это так, xargs имеет дело с двумя аргументами за раз, что xargs любые преимущества xargs . В этой версии мы предоставляем полное имя пути bash и имеем имя файла, вычисленное самим bash , а не полагаемся на find чтобы сделать это.

 find /mnt/test -name "*.txt" -exec bash -c './thulac < "$1" \ > "/mnt/tokenized/${1##*/}"' {} {} \; 

Генезис проблемы

 1. Good case when only 1 file present -print0 -printf '%f' /mnt/test/test.txt\0test.txt |-----------------|--------| arg0 = /mnt/test/test.txt arg1 = test.txt bash -c 'thulac < $0 > /mnt/tokenized/$1' thulac < /mnt/test/test.txt > /mnt/tokenized/test.txt 2. Error case when > 1 file present -print0 -printf '%f' /mnt/test/test.txt\0test.txt/mnt/test/test33.txt\0test33.txt |-----------------|-----------------------------|----------| arg0 = /mnt/test/test.txt arg1 = test.txt/mnt/test/test33.txt arg2 = test33.txt bash -c 'thulac < $0 > /mnt/tokenized/$1' thulac < /mnt/test/test.txt > /mnt/tokenized/test.txt/mnt/test/test33.txt 

исправлять

 We saw that the mixup occurred due to the absence of the delimiter '\0' in the -printf "%f" So the correct way is: find ... -print0 -printf "%f\0" | xargs ... Ensuring that the list is partitioned at the right places and the sequence of fullpath1+file1\0fullpath2+file2\0... is maintained. Now coming to the 'xargs' part, we write: xargs -0 -n 2 bash -c '...' 2 1 Points to observe are the following: a) '-0' => arguments to xargs will be taken to be NULL separated. b) -n 2 => we feed 2 args at a time to bash from the total pool delivered to xargs by find. c) 2 1 is just a best practice to get over different shell's behavior regarding what construes as $0, $1, $2, ...; In your particular case since you already know that $0 -> first arg, $1 -> 2nd arg, we could just as well have written what you did: find ... | xargs -0 -n 2 bash -c './thulac < $0 > /mnt/tokenized/$1' 

У вашей команды поиска есть проблема.
Чтобы разделить два имени, укажите пробел в формате printf

 find /mnt/test -name "*.txt" -print0 -printf " %f\n" ^ ( note the space above) 

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

 t=$(mktemp) find /tmp/test -name "*.txt" -exec sh -c ' if [ -s $1 ] then ./thulac < "$(<$1)" > "/mnt/tokenized/$2" else printf "%s" "$2" > "$1" fi' sh $t {} \; rm $t 

Если вам просто нужно передать путь и имя файла для каждого найденного файла, ответ будет проще, но только с использованием переносимых команд и синтаксиса (POSIX), то есть не зависит от bash, GNU find и GNU xargs:

 find /tmp/test -name "*.txt" -exec sh -c ' ./thulac < "$1" > "/mnt/tokenized/$(basename "$1")"' sh {} \; 

Обратите внимание, что {} нужно указывать только при использовании оболочки fish , что очень маловероятно.