Enquanto uso o BASH há vários anos, minha experiência com scripts BASH é relativamente limitada.
Meu código é como abaixo. Ele deve pegar toda a estrutura de diretórios de dentro do diretório atual e replicá-la para $OUTDIR
.
for DIR in `find . -type d -printf "\"%P\"\040"`
do
echo mkdir -p \"${OUTPATH}${DIR}\" # Using echo for debug; working script will simply execute mkdir
echo Created $DIR
done
O problema é que, aqui está uma amostra da minha estrutura de arquivos:
$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K
Observe os espaços: -S E for
utiliza parâmetros palavra por palavra, para que a saída do meu script seja algo como isto:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot
Mas eu preciso que ele pegue nomes de arquivos inteiros (uma linha de cada vez) da saída de find
. Eu também tentei find
colocar aspas duplas em torno de cada nome de arquivo. Mas isso não ajuda.
for DIR in `find . -type d -printf "\"%P\"\040"`
E saída com esta linha alterada:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"
Agora, eu preciso de uma maneira que possa iterar dessa maneira, porque também desejo executar um comando mais complicado envolvendo gstreamer
cada arquivo em uma estrutura semelhante a seguir. Como devo fazer isso?
Edit: Eu preciso de uma estrutura de código que me permita executar várias linhas de código para cada diretório / arquivo / loop. Desculpe se eu não era clara.
Solução: inicialmente tentei:
find . -type d | while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done
Isso funcionou bem na maior parte do tempo. No entanto, descobri mais tarde que uma vez que os resultados de tubos no circuito durante a execução em um subnível, quaisquer variáveis definidas no circuito foram mais tarde indisponíveis que fez implementar um contador de erros bastante difícil. Minha solução final ( desta resposta no SO ):
while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done < <(find . -type d)
Isso mais tarde me permitiu incrementar condicionalmente variáveis dentro do loop, que permaneceriam disponíveis posteriormente no script.
/
caracteres imprimíveis. Mas tudo é permitido, exceto/
e,\0
portanto, você deve permitir.Respostas:
Você precisa
find
inserir owhile
loop em um loop.Além disso, você não precisará usar
-printf
neste caso.Você pode fazer essa prova de arquivos com novas linhas em seus nomes, se desejar, usando um delimitador de nulos (que é o único caractere que não pode aparecer em um caminho de arquivo * nix):
Você também encontrará o uso em
$()
vez de reticulares mais versátil e fácil. Eles podem ser aninhados com muito mais facilidade e a citação pode ser feita com muito mais facilidade. Este exemplo artificial ilustrará estes pontos:Tente fazer isso com retalhos.
fonte
"$dir"
, é preferível usar"${dir}"
- é fácil dizer a diferença entre $ {dir} name e $ {dirname}, mas $ dirname poderia ser interpretado de qualquer maneira.read
lê uma linha inteira${dir}
, para que o IFS não importe.find … -print0 | xargs -0 …
porque o delimitador usado corresponde exatamente ao único caractere que não é permitido nos nomes de caminho POSIX: NUL (U + 0000).while
. @ Chris Johnsen: É verdade, mas mesmo os programas de ripar músicas não tendem a colocar feeds de linha em seus nomes de arquivos. E se o fizerem, eu quero saber (ou seja: algo der errado) e se livrar deles imediatamente ...Veja esta resposta que escrevi há alguns dias para um exemplo de script que lida com nomes de arquivos com espaços.
Existe uma maneira um pouco mais complicada (mas mais concisa) de alcançar o que você está tentando fazer:
-print0
diz ao find para separar os argumentos com um nulo; o -0 a xargs diz para ele esperar argumentos separados por nulos. Isso significa que ele lida bem com os espaços.-I {}
diz ao xargs para substituir a string{}
pelo nome do arquivo. Isso também implica que apenas um nome de arquivo deve ser usado por linha de comando (o xargs normalmente inclui o número que couber na linha)O resto deve ser óbvio.
fonte
O problema que você está enfrentando é que a instrução for está respondendo à localização como argumentos separados. O delimitador de espaço. Você precisa usar a variável IFS do bash para não dividir no espaço.
Aqui está um link que explica como fazer isso.
Defina sua localização para gerar seu delimitador de campo após o% P e defina seu IFS adequadamente. Escolhi ponto-e-vírgula, pois é pouco provável que você encontre seus nomes de arquivo.
A outra alternativa é chamar mkdir diretamente da localização, via
-exec
você pode pular o loop for completamente. Isso se você não precisar fazer nenhuma análise adicional.fonte
/
no POSIX e:
nos sistemas de arquivos DOS. Existem caracteres ilegais para diferentes sistemas de arquivos que você pode escolher para o IFS. Qualquer coisa mais complicada e é melhor você usar perl.find
retorna nomes de arquivos com caminhos, incluindo uma barra. Tente alterar o ponto-e-vírgula no seu script para uma barra e o eco imprimirá o diretório e o nome do arquivo em linhas separadas.while
, mas isso também parece bastante viável. Sim, em minha estrutura semelhante mais tarde eu precisei fazer uma análise mais aprofundada. (O nome do arquivo de entrada seria .ogg, que seria passado comofilesrc
no pipeline gst, mas um final equivalente em .mp3 baseado no diretório de saída seria gerado e também passado para o pipeline comofilesink
, e é claro que isso precisa ser feito para cada arquivo, juntamente com algunsecho
para o usuário.)Se o corpo do seu loop for mais do que um único comando, é possível usar o xargs para conduzir um script de shell:
Certifique-se de incluir o traço final (ou algum outro 'palavra') se o reservatório é da variedade Bourne / POSIX (ele é usado para definir $ 0 no script shell). Além disso, é preciso ter cuidado com a citação, pois o script do shell está sendo escrito dentro de uma cadeia de caracteres citada, em vez de diretamente no prompt.
fonte
na sua pergunta atualizada você tem
isso deve ser
fonte
fonte
ou para tornar a coisa toda muito menos complicada:
isso replica a estrutura de diretórios do SRC no horário de verão.
fonte
while
...do
...done
estrutura mais tarde para fazer o processamento similar de encontrar, o que exigiria várias linhas de código para ser executado em cada arquivo (modificar corda, eco, gst-launch, etc. ) ersync
não conseguiria isso. Foi por isso que especifiquei que precisava executar um conjunto de comandos mais complicado dentro de uma estrutura semelhante. Meu script usa essa estrutura de loop duas vezes, então, para a pergunta, postei a que apresentava menos crude no meio.Se você possui o GNU Parallel http: // www.gnu.org/software/parallel/ instalado, você pode fazer isso:
Assista ao vídeo de introdução do GNU Parallel para saber mais: http://www.youtube.com/watch?v=OpaiGYxkSuQ
fonte