Por que o valor do IFS é removido do arquivo durante a divisão?

2
IFS=$'?\n'
for line in $(cat "./newfiletoread")
do printf "${line}\n"
done

O conteúdo do arquivo é o seguinte: Olá, como você está? Como está a vida? O meu é tão chato quanto uma manhã de inverno!

O código acima divide o conteúdo do arquivo quando se depara com um '?' ou '!' ou '/ n' que é legal. Mas, durante a expansão, o shell remove esses caracteres do arquivo. O abaixo é a saída que recebo.

Hello there 
How are you doing
How is life
Mine is as boring as a winter morning

Eu entendo que é como o shell funciona substituindo esses valores do IFS por espaços antes que o comando seja executado. Existe uma maneira de preservar esses caracteres delimitadores durante a divisão? Eu gostaria de obter minha saída como esta abaixo:

Hello there! 
How are you doing?
How is life?
Mine is as boring as a winter morning!
    
por Ammu 21.08.2017 / 02:43

1 resposta

1

Primeiro, não leia as linhas de um arquivo com for

Eu li isso em algum lugar sobre a divisão de strings: use split quando você sabe o que jogar fora; use uma expressão regular quando você souber o que deseja manter. Ou algo assim.

O problema com o uso da divisão de palavras shell usando $IFS é que qualquer caractere nessa variável é usado para dividir, e você não pode saber qual deles.

Com o bash, você pode escrever:

line='Hello there! How are you doing? How is life? Mine is as boring as a winter morning!'
line=${line//\?/$'"?\n'}
line=${line//\!/$'"!\n'}
echo "$line"
Hello there"!
 How are you doing"?
 How is life"?
 Mine is as boring as a winter morning"!

Observe os espaços principais. Isso pode ser contornado com um padrão mais complicado: line=${line//\?*([[:blank:]])/$'"?\n'}

Eu usaria sed :

line='Hello there! How are you doing? How is life? Mine is as boring as a winter morning!'
new=$( sed 's/[?!][[:blank:]]*/&\n/g' <<<"$line" )
echo "$new"
Hello there! 
How are you doing? 
How is life? 
Mine is as boring as a winter morning!

tem uma função split() que permite capturar o separadores, mas usá-lo é bem detalhado:

echo "$line" | awk '{
    n = split($0, words, /[!?][[:blank:]]*/, seps)
    for (i = 1; i < n; i++) 
        print words[i] seps[i]
    print words[n]}
'
    
por 21.08.2017 / 14:07