Eu tenho o arquivo.txt que preciso ler em uma matriz Bash. Em seguida, preciso remover espaços, aspas duplas e tudo, exceto a primeira vírgula em cada entrada . Veja o quão longe eu cheguei:
$ cat file.txt 10,this 2 0 , i s 30,"all" 40,I 50,n,e,e,d,2 60",s e,e" $ cat script.sh #!/bin/bash readarray -t ARRAY<$1 ARRAY=( "${ARRAY[@]// /}" ) ARRAY=( "${ARRAY[@]//\"/}" ) for ELEMENT in "${ARRAY[@]}";do echo "|ELEMENT|$ELEMENT|" done $ ./script.sh file.txt |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,n,e,e,d,2| |ELEMENT|60,se,e|
O que funciona muito bem, exceto para a situação de vírgula. Estou ciente de que existem várias maneiras de esfolar esse gato, mas devido ao script maior do qual isso faz parte, eu realmente gostaria de usar a substituição de parâmetro para chegar aqui:
|ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see|
Isso é possível por meio da substituição de parâmetro?
Comentários
- Há algum motivo para você precisar manter o texto em uma matriz e por que você pode ‘ deixar, por exemplo,
awk
oused
fazem o processamento dos dados? - @Jeff – O loop na matriz será um pesadelo para implementar no script maior em que ‘ estou trabalhando.
- @JonRed Eu não ‘ não sei o que você está fazendo, então ‘ é inteiramente possível que você não tenha escolha no assunto, mas geralmente, quando você se pega fazendo tais acrobacias complexas de cordas no shell, que ‘ uma indicação muito boa de que você deve usar uma linguagem de programação real. O shell não foi projetado como uma linguagem de programação e, embora possa ser usado como tal, realmente não é ‘ uma boa idéia para coisas mais complexas. Eu recomendo fortemente que você considere mudar para perl ou python ou qualquer outra linguagem de script.
- @terdon É ‘ engraçado, acabei de dizer quase o exato mesma coisa com meu colega antes de ler este post. Eu basicamente disse que esta é a versão final deste script e que quaisquer requisitos adicionais exigirão a reescrita em Perl. Então, sim, eu definitivamente concordo
Resposta
Eu removeria o que você precisa remover usando sed
antes de carregar na matriz (observe também os nomes de variáveis em minúsculas, em geral é melhor evitar variáveis em maiúsculas em scripts de shell):
#!/bin/bash readarray -t array< <(sed "s/"//g; s/ *//g; s/,/"/; s/,//g; s/"/,/" "$1") for element in "${array[@]}";do echo "|ELEMENT|$element|" done
Isso produz a seguinte saída em seu arquivo de exemplo:
$ foo.sh file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see|
Se você realmente deve usar o parâmetro substituição, tente algo assim:
#!/bin/bash readarray -t array< "$1" array=( "${array[@]// /}" ) array=( "${array[@]//\"/}" ) array=( "${array[@]/,/\"}" ) array=( "${array[@]//,/}" ) array=( "${array[@]/\"/,}" ) for element in "${array[@]}"; do echo "|ELEMENT|$element|" done
Comentários
Resposta
Pelo que eu posso ver, não há necessidade de leia-o em uma matriz bash
para criar essa saída:
$ sed "s/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/" <file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see|
O sed
expression exclui espaços e aspas duplas, substitui a primeira vírgula por um espaço (não há outros espaços na string neste ponto), exclui todas as outras vírgulas, restaura a primeira vírgula e os prefixa e acrescenta os dados extras .
Alternativamente, com GNU sed
:
sed "s/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/" <file
(padrão sed
não suporta a combinação de 2
e g
como sinalizadores para s
comando).
Comentários
- com GNU sed, você pode usar
's/,//2g
para remover vírgulas, começando com o segundo - E os últimos dois comandos s /// podem ser
s/.*/|ELEMENT|&|/
mas isso pode ser mais difícil para o sed. - @glennjackman Possivelmente, mas parece bem legal.
- Sim, isso é parte de um script maior. A matriz é necessária, não apenas para a saída. Daí o meu interesse na substituição de parâmetros. Eu poderia fazer um loop no array com isso, mas será um pesadelo para implementar. Terndon forneceu uma solução sem loop usando sed que eu ‘ provavelmente recorrerei se a substituição de parâmetro for proibida.
- Se eu não fosse ‘ t vinculado ao uso de uma matriz, no entanto, esta seria a melhor solução.
Resposta
ELEMENT="50,n,e,e,d,2" IFS=, read -r first rest <<<"$ELEMENT" printf "%s,%s\n" "$first" "${rest//,/}"
50,need2
Abandone o hábito de usar nomes de variáveis ALLCAPS. Você eventualmente irá colidir com uma variável de “sistema” crucial como PATH e quebrar seu código.
Comentários
- Não substituição de parâmetro. MAS, eu não sabia que nomes de variáveis ALLCAPS eram um mau hábito no Bash. Você fez um bom argumento, que uma pesquisa rápida no Google definitivamente confirma. Obrigado por melhorar meu estilo! 🙂
- Eu ‘ respondi a perguntas em que a pessoa escreveu
PATH=something; ls $PATH
e depois perguntou sobrels: command not found
erro. - Existem quase uma centena de variáveis integradas que são nomeadas em maiúsculas (clique nesta página de manual link ) para ver …
Resposta
[Este é essencialmente um mais desenvolvido versão da resposta de glenn jackmann ]
Construindo uma matriz associativa a partir da chave e valor retirados, usando a primeira vírgula como separador:
declare -A arr while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt for k in "${!arr[@]}"; do printf "|ELEMENT|%s,%s|\n" "$k" "${arr[$k]}" done |ELEMENT|20,is| |ELEMENT|10,this| |ELEMENT|50,need2| |ELEMENT|40,I| |ELEMENT|60,see| |ELEMENT|30,all|
Resposta
Você pode fazer um loop na matriz e usar uma variável intermediária:
for((i=0; i < "${#ARRAY[@]}"; i++)) do rest="${ARRAY[i]#*,}" ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}" done
Isso atribui a rest
a parte após a primeira vírgula; em seguida, concatenamos três peças de volta ao original variável:
- a parte antes da primeira vírgula
- uma vírgula
- a substituição em
rest
de cada vírgula por nada
Comentários
- Este foi meu primeiro pensamento e é simples o suficiente para o exemplo, mas isso é parte de um script maior, onde o array é enorme e ‘ s já faz loops e seria uma coisa inteira. Isso definitivamente funcionaria, mas seria muito complicado de implementar no projeto maior em que ‘ estou trabalhando.
- Razoável; Eu apenas tentei responder dentro das limitações (apenas expansão de parâmetro).
RANDOMTEXTTHATWILLNEVERBEINTHEFILE
.