.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'
/mnt/test
위 명령은 파일이 하나만 있는 경우에도 작동합니다.그러나
/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
.
어떻게 작동하게 만들까요?
답변1
find /tmp/test -name '*.txt' \
-exec bash -c './thulac < "$(readlink -f {})" > "/mnt/tokenized/$(basename {})"' \;
find를 사용하여 파일을 검색하고 결과에 대해 명령을 실행합니다. 이 방법으로 bash -c 'command'
여러 $()를 실행할 수 있습니다.
readlink -f {}
결과를 생성하는 데 사용되는 전체 경로입니다.
basename {}
결과에서 경로를 제거 하는 데 사용됩니다 .
답변2
작업할 때 xargs
항상 "-"로 시작하고 이중 공백 "and"를 포함하는 입력으로 솔루션을 테스트해야 합니다. 이는 xargs
이러한 문제를 처리하는 데 악명이 높기 때문입니다.
mkdir -- '-" '"'"
seq 10 > ./-\"\ \ \'/'-" '"'".txt
GNU Parallel을 사용하는 솔루션은 다음과 같습니다.
find . -name "*.txt" -print0 |parallel -0 ./thulac '<' {} '>' {/}
< 와 > 는 따옴표로 묶어야 합니다. 그렇지 않으면 시작 쉘에서 해석됩니다 parallel
. 우리는 그것들이 시작 쉘에 의해 해석되기를 원합니다 parallel
.
답변3
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
올바른 방법으로 이를 수행할 수 있도록 하려고 합니다.
그렇지 않으면 한 파일의 전체 경로 이름이 다음 파일의 기본 이름으로 병합되며, 이는 여러 파일 이름에서 관찰되는 현상입니다!
그런 다음 에 한 번에 2개의 매개변수를 제공해야 합니다. bash alligator
그렇지 않으면 가능한 한 많은 매개변수를 사용하지만 실행 파일에는 처음 두 개의 매개변수만 전달됩니다 ./thulac
.
더 나은 옵션은 xargs가 한 번에 2개의 인수를 처리하므로 xargs xargs
에서 모든 작업을 수행하는 것 입니다. 이 버전에서는 이를 수행하는 대신 전체 경로 이름을 제공 하고 파일 이름을 직접 계산합니다 .find
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'
답변4
스크립트가 구현해야 하는 것이 무엇인지 정확히 말하지는 않지만 모든 홀수 파일을 첫 번째 인수로 전달하고 모든 짝수 파일 이름을 두 번째 인수로 전달한다고 가정하면 이식 가능한 방식으로 이를 수행하는 방법은 다음과 같습니다.
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 {} \;
인용 은 {}
shell 을 사용할 때만 필요하며 fish
이는 극히 드문 시나리오입니다.