0
votes

Syntaxe et utilisation correctes de la commande `cat`?

(Cette question fait suite à ce commentaire, dans une réponse à propos des hooks git)

Je suis bien trop peu qualifié en bash (jusqu'à présent) pour comprendre pleinement la remarque et comment agir en conséquence. Plus précisément, on m'a conseillé d'éviter d'utiliser la commande bash cat de cette façon:

tmp = "$1"
echo "$current_branch" $(cat $tmp) > "$1"

car l'ordre des opérations dépend du shell spécifique et cela pourrait finir par détruire le contenu de l'argument passé, donc le message de validation lui-même si je l'ai bien compris?

Aussi, comment "enregistrer le contenu dans une étape séparée"?

Est-ce que ce qui suit aurait du sens?

echo "$current_branch" $(cat "$1") > "$1"


0 commentaires

3 Réponses :


1
votes

À mon avis, il vaut mieux enregistrer dans un autre fichier.

Vous pouvez essayer quelque chose comme

echo "$current_branch" > tmp
cat "$1" >> tmp # merge these into 
# echo "$current_branch" $(cat "$1") > tmp
# may both OK
mv tmp "$1"

Cependant, je ne suis pas sûr si je comprends bien, ou il existe de meilleures solutions.

C'est ce que je considérais comme le cœur de la question. Il est difficile de décider de la "priorité" du bloc $ () et de > . Si > est exécuté "plus tôt", alors echo "$ current_branch" réécrira le fichier "$ 1" et supprimera le contenu original de "$ 1" , ce qui est un désastre. Si $ () est exécuté "plus tôt", alors tout fonctionne comme prévu. Cependant, il existe un risque et nous devons l’éviter.


1 commentaires

Je vais mieux maintenant, merci pour les explications. Je vais l'essayer et jouer avec.



5
votes

Le problème proposé ne concerne pas l'écrasement de variables ou d'arguments, mais le fait que lire et écrire dans un fichier en même temps est généralement une mauvaise idée.

Par exemple, cette commande peut sembler écrivez simplement un fichier sur lui-même, mais à la place, il le tronque:

tmp=$(mktemp) || exit
echo "$current_branch $(cat "$1")" > "$tmp"
mv "$tmp" "$1"

Cependant, ce n'est pas un problème dans votre commande spécifique. Il est garanti de fonctionner dans un shell compatible POSIX car l'ordre des opérations spécifie que les redirections se produiront après les expansions:

  1. Les mots qui ne sont pas des affectations de variables ou des redirections doivent être développés. Si des champs restent après leur développement, le premier champ sera considéré comme le nom de la commande et les champs restants seront les arguments de la commande.

  2. Les redirections doivent être effectuées comme décrit dans Redirection.

Double-cependant , c'est encore un peu fragile dans le sens où des modifications apparemment inoffensives peuvent déclencher le problème, par exemple si vous vouliez exécuter sed sur le résultat. Puisque la redirection (> "$ 1" ) et la substitution de commande $ (cat "$ 1") sont désormais dans des commandes séparées, la définition POSIX ne vous sauve plus: p >

# Command will now always delete the original message
modify_message() {
  echo "$current_branch $(cat "$1")"
}
modify_message "$1" > "$1"

De même, si vous le refactorisez en une fonction, il cessera également de fonctionner soudainement:

# Command may now randomly result in the original message being deleted
echo "$current_branch $(cat "$1")" | sed -e 's/(c)/©/g' > "$1"

Vous pouvez éviter cela en écrivant dans un fichier temporaire, puis remplacez votre original.

cat myfile > myfile   # Truncates the file to size 0


3 commentaires

Réponse exceptionnelle, merci beaucoup! Question de suivi: le fichier temporaire ne serait-il pas suspendu par la suite? Je veux dire, il ne devrait pas être détecté par git comme un fichier non suivi ou entraver le processus sinon, je devrais donc m'en débarrasser dans le processus, non?


@RomainValeri Dans cet exemple, mv est utilisé pour déplacer le fichier temporaire de votre hook et remplacer le fichier temporaire de git, il ne reste donc plus de fichier. Si vous aviez fait par exemple cat "$ tmp"> "$ 1" alors vous auriez raison: il aurait fallu un rm "$ tmp" séparé pour nettoyer le fichier temporaire.


Vous n'êtes pas le type autre , vous êtes le gars ;-)



1
votes

Un groupe de commandes serait bien mieux qu'une substitution de commandes ici. Notez la similitude avec la réponse de Geno Chen.

{
  echo "$current_branch"
  cat "$1"
} > tmp && mv tmp "$1"


2 commentaires

Puis-je simplement demander à quel point ce serait mieux? J'ai peur de ne pas savoir ce que vous appelez la substitution de commande ici: - /


$ (...) est une substitution de commande, et cela nécessite que tout le contenu du fichier soit d'abord lu en mémoire. (Il supprime également toutes les nouvelles lignes de fin, ce qui nécessite un peu de manipulation pour contourner.) Cela écrit simplement la sortie de la commande echo , suivie de la sortie du code cat commande, dans le fichier temporaire. (Sémantiquement, c'est la même chose que d'écrire puis d'ajouter à un fichier, mais il suffit d'ouvrir tmp une fois et est syntaxiquement plus propre.)