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?
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.
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 |
).
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"
.
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.
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 cat
sur 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
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
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
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
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").
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' "$@"
$
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 .
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")
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