Como ler um arquivo em uma variável no shell?

Eu quero ler um arquivo e salvá-lo em variável, mas eu preciso manter a variável e não apenas imprimir o arquivo. Como posso fazer isso? Eu escrevi este script, mas não é bem o que eu precisava:

#!/bin/sh while read LINE do echo $LINE done <$1 echo 11111----------- echo $LINE 

No meu script, eu posso dar o nome do arquivo como um parâmetro, assim, se o arquivo contiver “aaaa”, por exemplo, ele imprimiria isto:

 aaaa 11111----- 

Mas isso apenas imprime o arquivo na canvas e eu quero salvá-lo em uma variável! Existe uma maneira fácil de fazer isso?

Em multi-plataforma, menor denominador comum sh você usa:

 #!/bin/sh value=`cat config.txt` echo "$value" 

No bash ou zsh , para ler um arquivo inteiro em uma variável sem invocar o cat :

 #!/bin/bash value=$( 

Invocar cat em bash ou zsh para fazer slurp em um arquivo seria considerado um Useless Use of Cat .

Observe que não é necessário citar a substituição de comando para preservar as novas linhas.

Veja: Wiki do Bash Hacker - Substituição de Comando - Especialidades .

Se você quiser ler o arquivo inteiro em uma variável:

 #!/bin/bash value=`cat sources.xml` echo $value 

Se você quiser lê-lo linha por linha:

 while read line; do echo $line done < file.txt 

Duas armadilhas importantes

que foram ignorados por outras respostas até agora:

  1. Remoção de nova linha à direita da expansão de comando
  2. Remoção de caracteres NUL

Remoção de nova linha à direita da expansão de comando

Este é um problema para o:

 value="$(cat config.txt)" 

digite soluções, mas não para soluções baseadas em read .

Expansão de comando remove novas linhas à direita:

 S="$(printf "a\n")" printf "$S" | od -tx1 

Saídas:

 0000000 61 0000001 

Isso quebra o método ingênuo de ler arquivos:

 FILE="$(mktemp)" printf "a\n\n" > "$FILE" S="$(< "$FILE")" printf "$S" | od -tx1 rm "$FILE" 

Solução alternativa POSIX: acrescente um caractere extra à expansão de comando e remova-a depois:

 S="$(cat $FILE; printf a)" S="${S%a}" printf "$S" | od -tx1 

Saídas:

 0000000 61 0a 0a 0000003 

Quase solução POSIX: codificação ASCII. Ver abaixo.

Remoção de caracteres NUL

Não existe uma maneira sensata de armazenar os caracteres NUL nas variables .

Isso afeta as soluções de expansão e read e não conheço nenhuma boa solução alternativa para isso.

Exemplo:

 printf "a\0b" | od -tx1 S="$(printf "a\0b")" printf "$S" | od -tx1 

Saídas:

 0000000 61 00 62 0000003 0000000 61 62 0000002 

Ha, nosso NUL se foi!

Soluções Alternativas:

  • Codificação ASCII. Ver abaixo.

  • use liters de extensão $"" bash $"" :

     S=$"a\0b" printf "$S" | od -tx1 

    Funciona apenas para literais, portanto, não é útil para ler arquivos.

Solução alternativa para as armadilhas

Armazene uma versão codificada em base64 do uuencode do arquivo na variável e decodifique antes de cada uso:

 FILE="$(mktemp)" printf "a\0\n" > "$FILE" S="$(uuencode -m "$FILE" /dev/stdout)" uudecode -o /dev/stdout < (printf "$S") | od -tx1 rm "$FILE" 

Saída:

 0000000 61 00 0a 0000003 

uuencode e udecode são POSIX 7, mas não no Ubuntu 12.04 por padrão (pacote sharutils ) ... Eu não vejo uma alternativa POSIX 7 para a extensão de substituição do processo bash < () exceto gravar em outro arquivo ...

Claro, isso é lento e inconveniente, então eu acho que a verdadeira resposta é: não use o Bash se o arquivo de input puder conter caracteres NUL.

Como observa Ciro Santilli usando as substituições de comandos, as novas linhas serão baixadas. A solução alternativa deles para adicionar caracteres à direita é ótima, mas depois de usá-la por algum tempo, decidi que precisava de uma solução que não usasse a substituição de comandos.

Minha abordagem agora usa a read junto com o sinalizador -v do printf builtin para ler o conteúdo de stdin diretamente em uma variável.

 # Reads stdin into a variable, accounting for trailing newlines. Avoids needing a subshell or # command substitution. read_input() { # Use unusual variable names to avoid colliding with a variable name # the user might pass in (notably "contents") : "${1:?Must provide a variable to read into}" if [[ "$1" == '_line' || "$1" == '_contents' ]]; then echo "Cannot store contents to $1, use a different name." >&2 return 1 fi local _line _contents while read -r _line; do _contents="${_contents}${_line}"$'\n' done _contents="${_contents}${_line}" # capture any content after the last newline printf -v "$1" '%s' "$_contents" } 

Isso suporta inputs com ou sem novas linhas.

Exemplo de uso:

 $ read_input file_contents < /tmp/file # $file_contents now contains the contents of /tmp/file 

isso funciona para mim: v=$(cat ) echo $v