variables no comando find e mais problemas no shell [duplicado]

0

Eu escrevi o seguinte script:

#!/bin/bash
SAVEIFS=$IFS
alias export='export'
IFS=$(echo -en "\n\b")

          find $1 -name "*" -a -type f -exec sh -c let  len='expr length {}' \;  -exec  sh -c let  str='expr substr {} 1 len-3' \; -exec ffmpeg -y -i {}  $str.mp3 \;

# restore $IFS
unalias export
IFS=$SAVEIFS

Problema:

Você sabe quando mais um shell não pode exportar sua variável em outro shell, então eu preciso:

  1. use variáveis

  2. não use o shell de execução

Então, como eu faço isso?

Quando executar o seguinte script:

find $1 -name "*" -a -type f -exec let  len='expr length {}' \;  -exec let  str='expr substr {} 1 len-3' \; -exec ffmpeg -y -i {}  $str.mp3 \; 
# Note : above script doesn't has 'sh -c' 

Eu recebo o seguinte erro:

find: 'let': No such file or directory

Eu testei com export ou delete export ou deixe, descobri -exec tem problema com o comando shell embutido ....!

Você tem alguma ideia ????

    
por PersianGulf 08.01.2013 / 21:05

2 respostas

2

Você precisa cuidar de várias coisas

  • aspas duplas para proteger o conteúdo de uma variável da interpretação de shell. O IFS contém provavelmente por padrão "espaço + tab + nova linha", portanto: NEWIFS=IFS provavelmente acabará colocando nada em NEWIFS.

    SAVEIFS="$IFS"
    ...
    IFS="$(echo -en "\n\b")"   
    #yes, you can have " inside $( ... ) : the shell will gladly interpret them by
    # order of depth inside the $() construct. 
    # A big advantage over using backquotes where it would get mixed up and almost unreadable
    ...
    IFS="$SAVEIFS"
    
  • Não use palavras reservadas como nomes de variáveis (evite for let etc. Evite também muitos nomes de variáveis "comuns", por exemplo, LINES , COLUMNS , etc.

    • por que o aliasing export to export ? parece uma boa maneira de criar um loop se o shell for antigo ou, na melhor das hipóteses, inútil.

    • precising -name * significa que você evita qualquer nome de arquivo ou nome de diretório começando com . .

    • -exec sh .... irá lançá-lo em um sh subshell: qualquer variável que você declarar lá será perdida assim que sh sair para o shell de chamada.

Você precisa de algo como:

#!/bin/bash
for adirectory in "$@" ; do
    find "$adirectory" -type f -name '*.3gp' -print | while IFS= read -r wholeline ; do
       mylength=$(echo "$wholeline" | wc -c)  #Not needed, just kept as exemple of a way to achieve it
       myfilewithoutext="$(echo "$wholeline" | sed -e 's/.3gp$//' )"  
          #keeps only the filename without its ".3gp" or ".3GP" extension.
       ffmpeg -y -i "$wholeline"  "${myfilewithoutext}.mp3" > /dev/null   #?  please check if it works
    done #end of the find|while
done #end of the for looping on all the arguments passed to the script (dir names)

IFS em que while IFS= read -r wholeline ; do será definido como a string vazia APENAS durante a invocação do read -r wholeline . Intocado fora dela. VAR="something" command parameters invocará parâmetros de comando, com o valor de VAR temporariamente definido como something . Aqui, definimos o IFS como '' , ou seja, nada. Ele ainda será transmitido linha por linha e, como IFS está vazio, toda a linha acaba sendo lida por read wholeline .

    
por 08.01.2013 / 23:11
2

Como por comentário (convertendo * .3gp para * .mp3):

for n in *.3gp; do
    ffmpeg -y -i "$n"  "${n%.3gp}.mp3"
done

Conforme o comentário de olivier, para também atuar em arquivos em subdiretórios, *.3gp pode ser substituído por $(find ...) , desde que os nomes dos arquivos não precisem de escape (ou seja, eles não contêm espaços em branco ou caracteres globbing \[?* ) . Alternativamente, **/*.3gp pode ser usado (no bash, coloque shopt -s extglob no seu .bashrc ).

    
por 08.01.2013 / 22:58