Encontre fullpath e filename sob um diretório e depois passe para um arquivo executável como argumentos

5

Gostaria de encontrar o caminho completo e o nome do arquivo de todos os .txt em um diretório e passar para um arquivo executável ./thulac .

Me custou algum tempo para alcançar:

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

Mas isso só encontra caminho completo.

De xargs com vários argumentos Eu vejo:

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

O que eu quero alcançar é algo como:

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

Embora aqui, xargs não consiga dividir corretamente -print0 -printf "%f" como dois argumentos quando houver vários arquivos, o que me impediu.

Exemplo:

find /mnt/test -name "*.txt" -print0 -printf "%f" | \
   xargs -0 -I bash -c './thulac < $0 > /mnt/tokenized/$1'
  1. Se /mnt/test tiver apenas um arquivo, o comando acima funcionará.

  2. Mas se /mnt/test tiver mais de um arquivo (não importa a língua):

    [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
    

    Como você pode ver, xargs mistura dois caminhos juntos /mnt/tokenized/test.txt/mnt/test/test33.txt , o que leva ao erro No such file or directory .

Como fazer isso funcionar?

    
por Mithril 27.02.2017 / 09:50

5 respostas

2

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

Use find para procurar arquivos e executar comandos nos resultados. Com bash -c 'command' , você pode executar vários $ ().

Use readlink -f {} para criar o caminho completo para o resultado.

Use basename {} para remover o caminho do resultado.

    
por 06.03.2017 / 14:13
2

Ao trabalhar com xargs , você deve sempre testar suas soluções com entradas iniciadas por '-' e contendo espaço duplo 'e "porque xargs é famoso por lidar mal com elas:

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

Aqui está uma solução usando o GNU Parallel:

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

O < e > precisam ser citados, pois de outra forma seriam interpretados pelo shell que inicia parallel . Nós queremos que eles sejam interpretados pelo shell iniciado por parallel .

    
por 04.03.2017 / 11:44
2
find /mnt/test -name "*.txt" -print0 -printf "%f
find /mnt/test -name "*.txt" -exec bash -c './thulac < "$1" \
  > "/mnt/tokenized/${1##*/}"' {} {} \;
" | xargs -0 -n 2 bash -c 'shift $1; ./thulac < $1 > /mnt/tokenized/$2' 2 1

Você também deseja passar o nome do caminho completo com o delimitador nulo, para que, quando chegar a hora de xargs para desmontar a lista delimitada por nulo, pode fazê-lo de forma correta.

Caso contrário, o que acontecerá é que o nome do caminho completo de um arquivo será mesclado no nome de base do próximo arquivo, o fenômeno que você observou no caso de vários nomes de arquivos!

E então você precisa alimentar 2 argumentos por vez para o bash alligator , caso contrário, ele consumirá tantos quantos forem permitidos, mas passará apenas os dois primeiros para o executável ./thulac .

Uma alternativa melhor é dispensar xargs & faça todo o seu trabalho em find , pois como está, o xargs está lidando com 2 argumentos por vez, o que tira quaisquer vantagens de xargs . Nesta versão, fornecemos o nome completo do caminho para bash e temos o nome do arquivo computado por bash em vez de confiar em find para fazê-lo.

1. Good case when only 1 file present
-print0  -printf '%f'

 /mnt/test/test.txt
We saw that the mixup occurred due to the absence of the delimiter '
find /mnt/test -name "*.txt" -print0 -printf "%f
find /mnt/test -name "*.txt" -exec bash -c './thulac < "$1" \
  > "/mnt/tokenized/${1##*/}"' {} {} \;
" | xargs -0 -n 2 bash -c 'shift $1; ./thulac < $1 > /mnt/tokenized/$2' 2 1
' in the -printf "%f" So the correct way is: find ... -print0 -printf "%f
1. Good case when only 1 file present
-print0  -printf '%f'

 /mnt/test/test.txt
We saw that the mixup occurred due to the absence of the delimiter '%pre%' in the -printf "%f"
So the correct way is:
find ... -print0 -printf "%f%pre%" | xargs ...
Ensuring that the list is partitioned at the right places and the 
sequence of fullpath1+file1%pre%fullpath2+file2%pre%... 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'
test.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%pre%test.txt/mnt/test/test33.txt%pre%test33.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
" | xargs ... Ensuring that the list is partitioned at the right places and the sequence of fullpath1+file1%pre%fullpath2+file2%pre%... 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'
test.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%pre%test.txt/mnt/test/test33.txt%pre%test33.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

Gênese do problema

%pre%

Corrigir

%pre%     
por 05.03.2017 / 01:55
1

É o seu comando find que tem um problema.
Para separar os dois nomes, inclua espaço no formato printf

find /mnt/test -name "*.txt"  -print0 -printf " %f\n"
                                               ^ ( note the space above)
    
por 01.03.2017 / 12:21
1

Você não diz exatamente o que seu script precisa alcançar, mas supondo que você queira passar cada arquivo ímpar como primeiro argumento e cada nome de arquivo como segundo argumento, veja como fazer isso de uma maneira portátil:

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

Se você quiser apenas passar o caminho e o nome de arquivo de todos os arquivos encontrados, a resposta é mais simples, usando apenas comandos e sintaxe portáveis (POSIX), ou seja, não dependendo do bash, GNU find e GNU xargs:

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

Observe que {} precisa ser citado apenas ao usar o fish shell, uma situação muito improvável.

    
por 06.03.2017 / 09:25