Comment lire un fichier ou stdin en Bash?
En Perl, le code suivant va lire à partir du fichier spécifié sur la ligne de commande args ou à partir de stdin:
while (<>) {
print($_);
}
C'est très pratique. Je veux juste savoir quelle est la façon la plus simple pour lire à partir du fichier ou de l'entrée standard stdin dans bash.
Vous devez vous connecter pour publier un commentaire.
La solution suivante se lit à partir d'un fichier si le script est appelé à
avec un nom de fichier en tant que premier paramètre
$1
autrement qu'à partir de l'entrée standard.La substitution
${1:-...}
prend$1
si défini autrementle nom de fichier de l'entrée standard du processus est utilisé.
/proc/$$/fd/0
et/dev/stdin
? J'ai remarqué que le dernier semble être plus fréquente et semble plus simple./dev/stdin
plus simple, c'est aussi portables (ne nécessite pas le/proc
système de fichiers):< "${1:-/dev/stdin}"
-r
à votreread
de commande, de sorte qu'il n'est pas accidentellement manger\
caractères; utilisationwhile IFS= read -r line
pour préserver attaque et de fuite des espaces./dev/stdin: No such file or directory
utiliser Ubuntu 14.04.3 LTS .../bin/sh
- êtes-vous en utilisant un shell est autre que lebash
oush
?/dev/stdin
est alimenté par un tuyau de dans un autre script. Je reçois leNo such file or directory
erreur. Le script fonctionne très bien appelé en ligne de commande mais.cat
le fait déjà. Très souvent, le traitement pourrait prendre place dans un script Awk et la coquillewhile read
boucle juste complique les choses. Évidemment, il y a des situations où vous avez besoin pour traiter une ligne à la fois à partir d'un fichier dans une boucle shell, mais si vous avez juste trouvé cette réponse dans Google, vous devez être conscient que c'est une newbie antipattern.cat < "${1:-/dev/stdin}" > ${INPUT_FILE}
afin de l'utiliser dans plus d'une ocassion dans le script.Peut-être la solution la plus simple est de rediriger l'entrée standard avec une fusion de redirection de l'opérateur:
Stdin est le fichier descripteur de zéro. Le ci-dessus envoie l'entrée de canalisation à votre script bash en moins de stdin.
Lire plus sur le descripteur de fichier de redirection.
<&0
dans cette situation - votre exemple de travail sera le même avec ou sans elle - apparemment, les outils vous appelez depuis un script bash par défaut, voir la même stdin que le script lui-même (sauf si le script, il consomme en premier).less "$@" <&0
less -+S ; cat
je ne reçois stdin les canalisations de à moins, ce qui est agréable, mais rien de cat.<&0
Semble potentiellement astuce utile si vous voulez vous assurer que certains de commande obtient les canalisations...cependant je ne sais pas dans quelle situation que ce serait utile, comme mes expériences semblent indiquer que le premier programme gets stdin et il n'a pas d'action ou de scission entre invoquée programmes.Ici est la façon la plus simple:
Utilisation:
À attribuer stdin à la variable, vous pouvez utiliser:
STDIN=$(cat -)
ou tout simplementSTDIN=$(cat)
que l'opérateur n'est pas nécessaire (comme par @mklement0 commentaire).À analyser chaque ligne de la entrée standard, essayez le script suivant:
À lire à partir du fichier ou de stdin (si l'argument n'est pas actuellement), vous pouvez l'étendre à d':
Voir: Comment lire stdin quand aucun argument n'est passé? à stackoverflow SE
[ "$1" ] && FILE=$1 || FILE="-"
àFILE=${1:-- }
. (Chipoter: mieux vaut éviter les majuscules shell variables pour éviter les collisions de noms avec environnement variables.)${1:-- }
est POSIX, donc cela devrait fonctionner dans tous les POSIX, comme des coquillages. Ce qui ne fonctionne pas dans toutes ces coquilles est un processus de substitution (<(...)
), il va travailler dans bash, ksh, zsh, mais pas dans le tableau de bord, par exemple. Aussi, il vaut mieux ajouter-r
à votreread
de commande, de sorte qu'il n'est pas accidentellement manger\
caractères; ajouterIFS=
pour préserver attaque et de fuite des espaces.while IFS= read -r line
doit travail - ce que le shell que vous utilisez? Remarque l'espace après le=
; ce que ce n'est définie$IFS
à la chaîne vide pour laread
commande uniquement, qui est, le mondial$IFS
valeur ne change pas, ce qui est l'intention. En revanche, votre version -while IFS=; read -r line
- t modifier mondiale (parce queIFS=;
est séparée de la commande, pas un par commande temporaire-environnement-variable-réglage du préambule).$line
dans leecho
commande a été le problème.echo
: si une ligne est constituée de-e
,-n
ou-E
, il ne sera pas montré. Pour résoudre ce problème, vous devez utiliserprintf
:printf '%s\n' "$line"
. Je n'ai pas l'inclure dans mon édition précédente... trop souvent mes modifications sont rollbacked quand j'ai corrigé cette erreur:(
.--
est inutile si le premier argument est'%s\n'
IFS=
avecread
etprintf
au lieu deecho
.:)
.Je pense que c'est le straight-forward façon:
--
--
read
lit l'entrée standard stdin par défaut, donc pas besoin pour< /dev/stdin
.La
echo
solution consiste à ajouter de nouvelles lignes à chaque fois queIFS
rompt le flux d'entrée. @mgf réponse peut être un peu modifié:read
comportement: toutread
t potentiellement divisé en plusieurs jetons par les chars. contenues dans$IFS
, il retourne uniquement un unique jeton si vous ne spécifiez qu'un unique nom de la variable (mais les mic et de l'attaque et de fuite des espaces par défaut).read
et$IFS
-echo
lui-même ajoute de nouvelles lignes sans le-n
drapeau. "L'écho utilitaire écrit de toute opérandes, séparés par un espace simple (` ') caractères et suivie par un retour à la ligne (`\n') de caractères sur la sortie standard."\n
ajouté parecho
: Perl$_
comprend la ligne fin\n
à partir de la ligne de lire, alors que bash estread
ne le fait pas. (Cependant, comme @gniourf_gniourf points d'ailleurs, le plus robuste approche consiste à utiliserprintf '%s\n'
en lieu et place deecho
).Le Perl boucle dans le lit de la question de tous le nom du fichier arguments sur la ligne de commande, ou de l'entrée standard si aucun fichier n'est spécifié. Les réponses que je vois tous semblent processus d'un seul fichier ou l'entrée standard si il n'existe pas de fichier spécifié.
Bien que souvent tourné en dérision précisément que UUOC (Inutile d'Utiliser de
cat
), il ya des moments oùcat
est le meilleur outil pour le travail, et il est permis de penser que c'est l'un d'entre eux:Le seul inconvénient c'est qu'il crée un pipeline dans un sous-shell, donc les choses comme des assignations de variables dans le
while
boucle ne sont pas accessibles à l'extérieur de la canalisation. Lebash
moyen de contourner cela est Processus De Substitution:Ce qui laisse la
while
boucle dans la coque principale, de sorte que les variables définies dans la boucle sont accessibles en dehors de la boucle.>>EOF\n$(cat "$@")\nEOF
. Enfin, un bémol:while IFS= read -r line
est une meilleure approximation de ce quewhile (<>)
n'en Perl (conserve attaque et de fuite blanc, bien que Perl garde aussi les de fuite\n
).Perl comportement, avec le code donné dans l'OP peut prendre aucune ou plusieurs arguments, et si un argument est un seul trait d'union
-
cela est compris comme stdin. En outre, il est toujours possible d'avoir le nom de fichier avec$ARGV
.Aucune des réponses apportées jusqu'à présent vraiment imiter Perl comportement dans ces domaines. Voici un pur Bash possibilité. L'astuce est d'utiliser
exec
de façon appropriée.Nom de fichier est disponible en
$1
.Si aucun argument n'est donné, nous artificiellement fixé
-
que le premier paramètre de position. Nous avons ensuite une boucle sur les paramètres. Si un paramètre n'est pas-
, nous rediriger l'entrée standard de nom de fichier avecexec
. Si cette redirection réussit, nous boucle avec unwhile
boucle. Je suis l'aide de la normeREPLY
variable, et dans ce cas, vous n'avez pas besoin de réinitialiserIFS
. Si vous voulez un autre nom, vous devez réinitialiserIFS
comme (à moins, bien sûr, vous ne voulez pas que et savez ce que vous faites):Avec plus de précision...
IFS=
et-r
à laread
de commande fait en sorte que chaque ligne est en lecture non modifié (y compris l'attaque et de fuite des espaces).S'il vous plaît essayer le code suivant:
read
sansIFS=
et-r
, et les pauvres$line
sans ses citations.read -r
notation. OMI, POSIX se trompe; l'option devrait permettre à la signification spéciale pour la fuite des barres obliques inverses, ne désactivez pas, de sorte que les scripts existants (avant POSIX existé) n'aurait pas à se casser à cause de la-r
a été omis. J'observe, cependant, que c'était une partie de l'IEEE 1003.2 1992, qui a été la première version du shell POSIX et les utilitaires standard, mais il a été marqué comme une outre, même alors, donc, c'est de râler sur le long-gone possibilités. Je n'ai jamais couru dans le pétrin parce que mon code n'utilisez pas-r
; je dois avoir de la chance. Ignorez-moi.-r
devrait être la norme. Je suis d'accord qu'il est peu probable d'être dans les cas où il n'utilise pas conduit à des problèmes. Bien que, de code cassé est cassé le code. Mon montage a été déclenchée pour la première fois par cette pauvre$line
variable qui a gravement manqué à ses citations. J'ai corrigé leread
alors que j'étais à elle. Je n'ai pas corrigé leecho
parce que c'est le genre de montage qui obtient annulée.:(
.Code
${1:-/dev/stdin}
aurez simplement à comprendre au premier argument, donc, comment à ce sujet.Je ne trouve pas l'une de ces réponses acceptables. En particulier, l'on a accepté la réponse ne gère que le premier paramètre de ligne de commande et ignore le reste. Le programme en Perl que c'est en essayant d'imiter gère tous les paramètres de ligne de commande. Si l'on a accepté la réponse n'a même pas de répondre à la question. D'autres réponses utilisez bash extensions, ajouter inutiles "chat", les commandes, ne fonctionne que pour le cas simple d'écho à l'entrée à la sortie, ou sont tout simplement inutilement compliqué.
Cependant, je dois leur donner un peu de crédit, car ils m'ont donné quelques idées. Voici la réponse:
Les œuvres suivantes de la norme
sh
(Testé avecdash
sur Debian) et est tout à fait lisible, mais c'est une question de goût:De détails: Si le premier paramètre est non-vide, alors
cat
ce fichier, d'autrecat
l'entrée standard. Alors la sortie de l'ensemble de laif
instruction est traitée par lecommands_and_transformations
.cat "${1:-- }" | any_command
. La lecture de variables shell et en écho à eux peuvent travailler pour de petits fichiers, mais ne l'est pas si bien.[ -n "$1" ]
peut être simplifié à[ "$1" ]
.J'ai cumulé toutes les réponses ci-dessus, et a créé une fonction shell qui répondrait à mes besoins. C'est à partir d'un terminal cygwin de mes 2 Windows10 machines où j'ai eu un dossier partagé entre eux. J'ai besoin d'être en mesure de gérer les suivantes:
cat file.cpp | tx
tx < file.cpp
tx file.cpp
Où un nom de fichier est spécifié, j'ai besoin d'utilisé le même nom de fichier lors de la copie. Où les flux de données d'entrée a été canalisée par, alors j'ai besoin de générer un nom de fichier temporaire ayant les heures, minutes et secondes. L'partagé mainfolder a des sous-dossiers de la les jours de la semaine. C'est pour des raisons d'organisation.
Voici, l'ultime script pour mes besoins:
Si il n'y a aucun moyen que vous pouvez voir à optimiser ce, je voudrais savoir.
Comment sur
cat
sera placé dans la ligne de commande. La ligne de commande a une taille maximale. Aussi ce ne sera pas le lire ligne par ligne, mais mot par mot.