Digamos que eu tenha este arquivo:
hello world hello world
Este programa
#!/bin/bash for i in $(cat $1); do echo "tester: $i" done
saídas
tester: hello tester: world tester: hello tester: world
Eu “d gostaria de ter o for
itera sobre cada linha individualmente, ignorando os espaços em branco, ou seja, as duas últimas linhas devem ser substituídas por
tester: hello world
Usando aspas for i in "$(cat $1)";
resulta em i
sendo atribuído o arquivo inteiro de uma vez. O que devo mudar?
Resposta
(9 anos depois 🙂
Ambas as respostas fornecidas falhariam em arquivos sem uma nova linha no final, isso efetivamente pularia a última linha, não produziria erros, levaria ao desastre (aprendeu muito maneira :).
A melhor solução concisa que encontrei até agora que “Funciona simplesmente” (em bash e sh):
while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt
Para uma discussão mais aprofundada, consulte esta discussão StackOverflow: How to use “while leia “(Bash) para ler a última linha em um arquivo se não houver nova linha no final do arquivo?
Cuidado: esta abordagem adiciona uma nova linha adicional à última linha se houver nenhum já.
Comentários
- Boa captura, obrigado! Observe o " Esteja ciente de que isso adiciona uma nova linha adicional no EOF, se ainda não houver nenhuma. " comentário embora
- Tobias, eu ' adicionarei isso como uma nota, obrigado.
Resposta
Com for
e IFS :
#!/bin/bash IFS=$"\n" # make newlines the only separator set -f # disable globbing for i in $(cat < "$1"); do echo "tester: $i" done
Observe, entretanto, que ele irá pular linhas vazias como nova linha sendo um caractere IFS de espaço em branco, sequências dele contam como 1 e os anteriores e posteriores são ignorados. Com zsh
e ksh93
(não bash
), você pode alterá-lo para IFS=$"\n\n"
para que a nova linha não seja tratada de maneira especial, no entanto, observe que todos os caracteres de nova linha finais (de modo que inclua as linhas vazias finais) sempre serão removidos pela substituição do comando.
Ou com read
(não mais cat
):
#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1"
Lá, as linhas vazias são preservadas, mas observe que pularia a última linha se não fosse devidamente delimitada por um caractere de nova linha.
Comentários
- obrigado, eu não ' sabia que alguém poderia
<
em um loop inteiro. Embora faça perfeitamente sentido agora que vi - vejo
IFS \ read -r line' in second example. Is really
IFS = `necessário? IMHO é o suficiente para dizer:while read -r line; do echo "tester: $line"; done < "$1"
- @GrzegorzWierzowiecki
IFS=
desativa a remoção de espaços em branco à esquerda e à direita. Veja Emwhile IFS= read..
, por que o IFS não tem efeito? - @BenMares Para evitar globbing expressões que possivelmente aparecem no texto que estamos lendo sendo expandidas para nomes de arquivo correspondentes. Tente, por exemplo,
printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done
. - Um
while IFS= read -r line || [ "$line" ]; do
processará uma linha final não devidamente delimitada por um caractere de nova linha (mas será adicionado de volta).
Resposta
Para saber o que vale, eu preciso fazer isso com bastante frequência, e nunca consigo me lembrar a maneira exata de usar while IFS= read...
, então defini a seguinte função em meu perfil bash:
# iterate the line of a file and call input function iterlines() { (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; } local File=$1 local Func=$2 n=$(cat "$File" | wc -l) for (( i=1; i<=n; i++ )); do "$Func" "$(sed "${i}q;d" "$File")" done }
Esta função primeiro determina o número de linhas no arquivo, então usa sed
para extrair linha após linha, e passa cada linha como um único argumento de string para qualquer função. Suponho que isso pode ficar realmente ineficiente com arquivos grandes, mas isso não tem sido um problema para mim até agora (sugestões sobre como melhorar isso são bem-vindas, é claro).
O uso é muito bom IMO:
>> cat example.txt # note the use of spaces, whitespace, etc. a/path This is a sentence. "wi\th quotes" $End >> iterlines example.txt echo # preserves quotes, $ and whitespace a/path This is a sentence. "wi\th quotes" $End >> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string 1 1 1 1 1