web-dev-qa-db-fra.com

Comment lire un fichier dans une variable en shell?

Je veux lire un fichier et le sauvegarder dans une variable, mais je dois conserver la variable et ne pas simplement imprimer le fichier . Comment puis-je faire cela? J'ai écrit ce script mais ce n'est pas tout à fait ce dont j'avais besoin:

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

Dans mon script, je peux donner le nom du fichier en tant que paramètre. Ainsi, si le fichier contient "aaaa", par exemple, il affichera ceci:

aaaa
11111-----

Mais cela ne fait qu'imprimer le fichier à l'écran et je souhaite le sauvegarder dans une variable! Y a-t-il un moyen simple de le faire?

369
kaka

Dans la multiplate-forme, le plus petit commun dénominateur sh, vous utilisez:

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

Dans bash ou zsh, pour lire un fichier entier dans une variable sans appeler cat:

#!/bin/bash
value=$(<config.txt)
echo "$value"

Invoquer cat dans bash ou zsh à Slurp un fichier serait considéré comme un Utilisation inutile de Cat .

Notez qu'il n'est pas nécessaire de citer la substitution de commande pour conserver les nouvelles lignes.

Voir: Wiki de Bash Hacker - Substitution de commande - Spécialités .

817
Alan Gutierrez

Si vous voulez lire le fichier entier dans une variable:

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

Si vous voulez le lire ligne par ligne:

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

Deux pièges importants

qui ont été ignorés par d'autres réponses jusqu'à présent:

  1. Fin de suppression de nouvelle ligne de l'extension de commande
  2. Suppression du caractère NUL

Fin de suppression de nouvelle ligne du développement de la commande

C'est un problème pour le:

value="$(cat config.txt)"

type solutions, mais pas pour les solutions à base read.

L'extension de commande supprime les retours à la ligne:

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

Les sorties:

0000000 61
0000001

Cela rompt la méthode naïve de lecture de fichiers:

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

Solution palliative POSIX: ajoutez un caractère supplémentaire au développement de la commande et supprimez-le ultérieurement:

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

Les sorties:

0000000 61 0a 0a
0000003

Solution de contournement presque POSIX: ASCII encoder. Voir ci-dessous.

Suppression de caractères NUL

Il n'y a pas de moyen sûr Bash de stocker les caractères NUL dans les variables .

Cela affecte à la fois les solutions d’extension et read, et je ne connais aucune solution de contournement satisfaisante.

Exemple:

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

Les sorties:

0000000 61 00 62
0000003

0000000 61 62
0000002

Ha, notre NUL est parti!

Solutions de contournement:

  • ASCII encoder. Voir ci-dessous.

  • utilisez les littéraux de bash extension $"":

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

    Ne fonctionne que pour les littéraux, donc pas utile pour lire des fichiers.

Solution de contournement pour les pièges

Stockez une version encodée en base64 du fichier dans la variable et décodez-la avant chaque utilisation:

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

Sortie:

0000000 61 00 0a
0000003

uuencode et udecode sont POSIX 7 mais pas dans Ubuntu 12.04 par défaut (paquet sharutils) ... Je ne vois pas d'alternative POSIX 7 pour l'extension de substitution <() du processus bash, sauf l'écriture dans un autre fichier ...

Bien sûr, c'est lent et peu pratique, alors je suppose que la vraie réponse est: n'utilisez pas Bash si le fichier d'entrée peut contenir des caractères NUL.

cela fonctionne pour moi: v=$(cat <file_path>) echo $v

2
angelo.mastro

Comme Ciro Santilli note , l’utilisation de substitutions de commandes supprimera les retours à la ligne. Leur solution de contournement en ajoutant des caractères de fin est géniale, mais après l'avoir utilisée pendant un certain temps, j'ai décidé que j'avais besoin d'une solution qui n'utilisait pas du tout le remplacement de commande.

Mon approche utilise maintenant read avec l'indicateur printf builtin's -v afin de lire le contenu de stdin directement dans une variable.

# 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"
}

Ceci prend en charge les entrées avec ou sans nouvelles lignes.

Exemple d'utilisation:

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