bash executa o comando read do arquivo

1

Depois de algumas manipulações, recebi o arquivo com comandos que ficaria feliz em poder executar.

Arquivo de entrada (o nome do arquivo é inp2.txt):

"02 - Beautiful Emptiness.mp3"
"02 - Come. mp3"
"02 - Go For It.mp3"

Código:

#!/bin/bash
exec 4<inp2.txt           # opening  file via descriptor
while read LINE <&4; do
    printf "%s\n" "$LINE"  # just to watch that command is proper
    $LINE                # execute command
done

EDITAR: desculpe, omiti parte importante do código que achei não ser importante para o entendimento. Código completo:

#!/bin/bash
IFS='
 '
declare LINE
declare BUF
declare a
a=1 

rm inp2.txt # delete previous version of file
exec 3< inp.txt # open descriptor for a file()content of it is written above
while read LINE <&3; do  # read it
    printf "mv ""$LINE"" %s\n" """$a"".mp3" >> inp2.txt 
    # form a command and output it to the file, just to make sure that I got a command that I really want
    let "a++"  # increment number that will be a part of new unique name
done

exec 4<inp2.txt #open again descriptor with ready commands

while read LINE <&4; do
    printf "%s\n" "$LINE"  # check again
    $LINE #  here it should be executed, but I get mistakes that  is pointed down
done

exit 0

Saída:

Parece que o comando é adequado, mas algo está errado e não consigo descobrir exatamente o que está errado.

    
por Tebe 30.08.2012 / 01:17

5 respostas

8

O script, o arquivo de dados e a saída que você publicou são inconsistentes. Nem o script nem o arquivo de dados contêm mv , mas sua captura de tela é. Além disso, sua captura de tela menciona uma linha 28, que o script que você postou não tem. É difícil identificar seu problema quando você nos fornece informações inconsistentes.

Dito isto, você está tentando fazer uma das duas coisas, nenhuma das quais pode funcionar do jeito que você está tentando.

  • Se o arquivo de entrada contiver linhas como

    mv "02 - Beautiful Emptiness.mp3" 1.mp3
    

    então é realmente um script de shell. Em vez de ler linha por linha, execute-o como um script de shell. Certifique-se de que você pode confiar neste arquivo, já que você estará executando o que estiver lá, incluindo rm -rf ~ ou algo assim.

    . inp2.sh
    
  • Se o arquivo de entrada contiver linhas como

    "02 - Beautiful Emptiness.mp3"
    

    então a maneira como você está lendo não funciona. read LINE faz o seguinte:

    • leia uma linha;
    • se essa linha terminar com uma barra invertida, remova a barra invertida e leia outra linha (repita até que uma linha que não termine com \ foi lida);
    • substituir todas as seqüências de caracteres de barra invertida + caracteres pelo segundo caractere somente;
    • defina LINE para a concatenação das linhas lidas, menos as novas linhas.

    Quando o shell executa o comando $LINE , ele faz o que sempre faz quando vê uma substituição de variável fora das aspas, que é:

    • divida o valor da variável em uma lista de palavras em todos os lugares onde ele contém espaço em branco (assumindo o valor padrão de IFS );
    • trate cada palavra como um padrão glob e expanda-a se corresponder a pelo menos um arquivo.

    Parece inútil? Isto é. E note que não há nada sobre aspas aqui: as citações fazem parte da sintaxe do shell, elas não fazem parte das regras de expansão do shell.

O que você deve fazer é ter inp2.txt contendo uma lista de nomes de arquivos, um por linha. Veja Por que é 'while IFS = read' usado com muita frequência, em vez de 'IFS =; while read .. '? para saber como ler uma lista de linhas de um arquivo. Você estará querendo algo como

i=1
while IFS= read -r source; do
  dir=$(dirname -- "$source")
  ext=
  case "${source##*/}" in
    *.*) ext=.${source##*.};;
  esac
  mv -- "$source" "$dir/$i$ext"
done <inp2.txt

Apenas pela perfeição, mencionarei outra possibilidade, mas não a recomendo porque é complicada e não permite que você faça o que parece estar fazendo. Um arquivo como

"02 - Beautiful Emptiness.mp3" "02 - Come. mp3" foo\ bar.mp3

então ele pode ser lido pelo comando xargs . A entrada para xargs é uma lista de elementos delimitados por espaço em branco, que pode ser um literal (possivelmente contendo espaços em branco) entre aspas simples, um literal (possivelmente contendo espaço em branco) entre aspas duplas ou um literal sem aspas que pode conter escapes de barra invertida ( \ cita o próximo caractere). Observe que a sintaxe xargs é diferente de qualquer coisa que o shell possa reconhecer.

    
por 30.08.2012 / 03:07
2

Pelo que parece, você está tendo problemas com citações.

Envolvendo "$LINE" entre aspas (na linha 'executar comando'), que é tratado como um único comando, não o comando mv com alguns parâmetros adicionais. Então, para começar, você deve remover as aspas nessa linha.

Além disso, você terá problemas com seus caminhos: vejo que você tem nomes de arquivos com espaços neles. Então você terá que modificar seu arquivo inp2.txt para ter aspas em torno de quaisquer caminhos / nomes de arquivos com espaços neles, ou escapar dos espaços.

Além disso, você pode tentar adicionar o seguinte ao topo do seu script:

IFS='
'

Sim, a segunda linha é apenas uma cotação única nessa linha. Isso mudará como os espaços são manipulados no seu script.

Seu script seria assim:

#!/bin/bash
IFS='
'
exec 4<inp2.txt           # opening  file via descriptor
while read LINE <&4; do
 printf "%s\n" "$LINE"  # just to watch that command is proper
$LINE                # execute command
done
    
por 30.08.2012 / 01:23
1

Esta versão funciona construindo linhas de comando e inserindo-as no bash para execução.

#! /bin/bash 

i=1
while read LINE ; do
  # strip off any leading or trailing double-quotes.
  # script now works whether input quotes the filenames or not.
  LINE=$(echo "$LINE" | sed -r -e 's/^"|"$//g')
  [ -n "$LINE" ] && echo mv \"$LINE\" \"$i.mp3\"
  i=$((i + 1)) 
done < inp2.txt | bash

NOTA: para testar o script a seco, exclua | bash da linha final ou altere para | cat , que produz uma saída assim:

mv "02 - Beautiful Emptiness.mp3" "1.mp3"
mv "02 - Come. mp3" "2.mp3"
mv "02 - Go For It.mp3" "3.mp3"

O teste [ -n "$LINE" ] pula todas as linhas em branco na entrada ... adicionadas porque eu criei o meu inp2.txt com uma linha extra vazia no final.

    
por 30.08.2012 / 05:31
0

Obrigado pelas respostas e paciência para mim. A principal conclusão a que cheguei depois de ler as respostas é que eu deveria melhorar minhas habilidades bash.

Mas eu precisava de solução rápida (porque é apenas parte auxiliar de um projeto maior).

Então, minha solução é:

  #!/bin/bash
  ls *.mp3   > inp.txt  # input variables were input once

  declare LINE
  declare a
  a=1 

  rm inp2.txt
  exec 3< inp.txt
  while read LINE <&3; do
   printf "mv  "" '$LINE' "" %s\n"   "'$a.mp3'" >> inp2.txt
   let "a++"
  done



  exit 0

Depois disso, não pude me permitir reescrever código inteiro, então usei o Python:

  #!/usr/bin/python
  # -*- coding: utf-8 -*-

  import os 
  f=open("inp2.txt","r")  # just read command from file and execute them
  for line in f:
   print line 
   os.system(line)

De qualquer forma, não finjo que isso possa ser uma solução (coloquei vergonhosamente minha cabeça na areia e entendi que é uma solução ruim).

Apenas deixe estar aqui, talvez seja útil para alguém tentar usar o Bash depois de outros idiomas interpretados como eu.

    
por 30.08.2012 / 19:01
0

Você tentou algo assim?

while read LINE <&4; do
    printf "%s\n" "$LINE"  # just to watch that command is proper
    comm="$LINE"                # asssign command to a variable
    $comm                  # execute command
done

Funcionou para mim usando o bash.

    
por 15.05.2013 / 17:39