web-dev-qa-db-fra.com

Comment écrire un script qui accepte les entrées d'un fichier ou de stdin?

Comment écrire un script qui accepte les entrées d'un argument de nom de fichier ou de stdin?

par exemple, vous pouvez utiliser less de cette façon. on peut exécuter less filename et cat filename | less de manière équivalente.

y a-t-il un moyen facile de le faire "hors de la boîte"? ou dois-je réinventer la roue et écrire un peu de logique dans le script?

53
gilad hoch

Si l'argument de fichier est le premier argument de votre script, vérifiez qu'il existe un argument ($1) et qu'il s'agit d'un fichier. Sinon lire les entrées de stdin -

Donc, votre script pourrait contenir quelque chose comme ceci:

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

par exemple. alors vous pouvez appeler le script comme

./myscript.sh filename

ou

who | ./myscript.sh

Edit Quelques explications sur le script:

[ $# -ge 1 -a -f "$1" ] - Si au moins un argument de ligne de commande ($# -ge 1) AND (opérateur -a), le premier argument est un fichier (-f teste si "$ 1" est un fichier), le résultat du test est true.

&& est l'opérateur logique AND du shell. Si test est true, attribuez input="$1" et cat $input produira le fichier.

|| est l'opérateur logique Shell OR. Si le test est faux, les commandes qui suivent || sont analysées. l'entrée est assignée à "-". cat - lit à partir du clavier.

En résumé, si l'argument de script est fourni et qu'il s'agit d'un fichier, l'entrée de variable est affectée au nom du fichier. S'il n'y a pas d'argument valide, cat lit depuis le clavier.

57
suspectus

read lit à partir de l'entrée standard. Le fait de le rediriger depuis un fichier (./script <someinput) ou via un tube (dosomething | ./script) ne le fera pas fonctionner différemment.

Tout ce que vous avez à faire est de parcourir toutes les lignes en entrée (et cela ne diffère pas d’itérer sur les lignes du fichier).

(exemple de code, traite une seule ligne)

#!/bin/bash

read var
echo $var

Répondra à la première ligne de votre entrée standard (via < ou |).

12
Lukasz Daniluk

Le moyen le plus simple est de rediriger stdin vous-même:

if [ "$1" ] ; then exec < "$1" ; fi

Ou si vous préférez la forme plus laconique:

test "$1" && exec < "$1"

Maintenant, le reste de votre script peut simplement lire à partir de stdin. Bien sûr, vous pouvez faire de même avec une analyse d’options plus avancée plutôt que de coder en dur la position du nom de fichier en tant que "$1".

4
R..

Vous ne parlez pas de ce que vous comptez utiliser avec Shell, je vais donc supposer que c'est bash, bien que ce soient des choses assez standard pour tous les obus.

Arguments de fichier

Les arguments sont accessibles via les variables $1-$n ($0 renvoie la commande utilisée pour exécuter le programme). Supposons que je possède un script qui catsur n nombre de fichiers avec un séparateur entre eux:

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

Dans ce cas, nous passons un nom de fichier à cat. Toutefois, si vous souhaitez transformer les données du fichier (sans les écrire ni les réécrire explicitement), vous pouvez également stocker le contenu du fichier dans une variable:

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

Lire de stdin

En ce qui concerne stdin, la plupart des shells ont un read intégré assez standard, bien qu'il existe des différences dans la façon dont les invites sont spécifiées (au moins).

La page de manuel de Bash builtins contient une explication assez concise de read, mais je préfère la Bash Hackers page.

Simplement:

read var_name

Plusieurs variables

Pour définir plusieurs variables, fournissez simplement plusieurs noms de paramètres à read:

read var1 var2 var3

read placera ensuite un mot de stdin dans chaque variable, en plaçant tous les mots restants dans la dernière variable.

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

Si moins de mots sont entrés que de variables, les variables restantes seront vides (même si elles avaient été définies précédemment):

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

Instructions

J'utilise souvent le drapeau -p pour une invite:

read -p "Enter filename: " filename

Remarque: ZSH et KSH (et peut-être d'autres) utilisent une syntaxe différente pour les invites:

read "filename?Enter filename: " # Everything following the '?' is the Prompt

Les valeurs par défaut

Ce n'est pas vraiment une astuce read, mais je l'utilise beaucoup en conjonction avec read. Par exemple:

read -p "Y/[N]: " reply
reply=${reply:-N}

En gros, si la variable (réponse) existe, retourne elle-même, mais si est vide, retourne le paramètre suivant ("N").

4
Jacob Hayes

utilisez (ou enchaînez) quelque chose d'autre qui se comporte déjà de cette façon, et utilisez "$@"

disons que je veux écrire un outil qui remplacera des séries d'espaces dans le texte avec des onglets

tr est la manière la plus évidente de faire cela, mais il seulement accepte stdin, nous devons donc chaîner cat:

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

pour un exemple où l'outil utilisé se comporte déjà de cette façon, nous pourrions le réimplémenter avec sed à la place:

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 
3
Aaron Davies

Vous pouvez aussi faire:

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

Pour plus d’astuces de substitution de paramètres dans Bash, voir this .

3
Daniel

Le moyen le plus simple et compatible POSIX est:

file=${1--}

ce qui équivaut à ${1:--}.

Ensuite, lisez le fichier comme d'habitude:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
0
kenorb

Vous pouvez également rester simple et utiliser ce code


Lorsque vous créez un fichier de script pass_it_on.sh avec ce code,

#!/bin/bash

cat

Tu peux courir

cat SOMEFILE.txt | ./pass_it_on.sh

et tout le contenu de stdin sera simplement diffusé à l'écran.


Vous pouvez également utiliser ce code pour conserver une copie de stdin dans un fichier, puis la cracher à l'écran.

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

et voici un autre exemple, peut-être plus lisible, expliqué ici:

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

S'amuser.

RaamEE

0
RaamEE