Existe-t-il un autre moyen simple d'ajouter une ligne à la fin du fichier autre que `>>`?


21

Récemment, je fais écho à des phrases courtes dans un tree_holefichier.

J'utilisais echo 'something' >> tree_holepour faire ce travail.

Mais j'étais toujours inquiet de savoir si je saisissais mal >au lieu de >>, car je le faisais souvent.

J'ai donc créé mon propre basc global dans le bashrc:

function th { echo "$1" >> /Users/zen1/zen/pythonstudy/tree_hole; }
export -f th

Mais je me demande s'il existe un autre moyen simple d'ajouter des lignes à la fin d'un fichier. Parce que je devrai peut-être l'utiliser souvent à d'autres occasions.

Y a-t-il?


9
N'oubliez-vous pas que vous utilisez la solution de contournement à chaque fois que vous entrez>? Une fois que j'ai eu un alias rm="rm -i"et dans un autre environnement, j'ai écrit en rm *attendant les questions de confirmation. Vous apprenez des habitudes dangereuses!
Walter A

9
@WalterA er, sa "solution de contournement" ne lui permet pas de taper> au lieu de >>, il exécute simplement "th some_sentence", qui ne fait rien si l'alias n'est pas défini.
Random832

1
@ Ranom832 correct pour sa e solution de contournement. L'avertissement concerne une "solution" comme le noclobber. Quand il utilise quelque chose comme noclobber dans son shell normal, il peut utiliser> quand il est root temporaire et veut ajouter quelque chose.
Walter A

7
@ Random832 & WalterA Normalement, je ne dis rien à ce sujet quand je le vois, mais j'ai pensé qu'un avis amical de temps en temps pourrait être utile. Le profil utilisateur de Zen n'a pas beaucoup de détails, donc je ne sais pas si vous savez vraiment que sa "solution de contournement" est la bonne forme. Vous devriez peut-être dire leur «solution» ou la «solution» du PO . Peut-être connaissez-vous personnellement Zen et vous savez donc que le sien est correct, auquel cas veuillez pardonner le bruit. Ce n'est pas grave, je le mentionne simplement parce que je sais que je ne l'aurais pas beaucoup apprécié si vous aviez utilisé ce formulaire pour parler de moi.
Celada

1
@Zen, vous avez écrit que vous craigniez de mal saisir> au lieu de >>. Le plan de Celada enlèvera les risques dans votre environnement et est une bonne solution pour vous. Lorsque vous aidez votre voisin (qui n'a pas de noclobber ou utilise ksh) et que vous avez perdu votre attention pour un ou deux> caractères, vous pouvez accidentellement écraser l'un de ses fichiers. Donc, chaque fois que vous recevez l'avertissement de noclobber dans votre propre environnement, Dieu merci ou Celada, dites-vous: Ohh, soyez prudent s'il vous plaît !, secouez la tête et attendez deux secondes.
Walter A

Réponses:


47

Définissez l' noclobberoption du shell :

bash-3.2$ set -o noclobber
bash-3.2$ echo hello >foo
bash-3.2$ echo hello >foo
bash: foo: cannot overwrite existing file
bash-3.2$ 

3
Telle est la réponse définitive. Notez que vous pouvez toujours écraser le fichier si vous le forcez avec >|. zshactive ce comportement par défaut.
orion

1
@orion Comment l'activez-vous dans zsh? Je ne me souviens pas de l'avoir explicitement désactivé, mais l'écrasement fonctionne très bien sur mon zsh.
muru

2
C'est setopt noclobberet setopt clobber. Il semble que ce ne soit pas exactement "par défaut", cela dépend des fichiers de configuration fournis avec votre distribution.
orion

@muru devrait fonctionner avec set [+-]Cn'importe quel shell moderne
mikeserv

3
@Zen set +o noclobberen bash, ou set +C.
Ruslan

7

Si vous craignez que votre fichier soit endommagé par l' >opérateur, vous pouvez modifier votre attribut de fichier pour l'ajouter uniquement:
dans le système de fichiers ext2 / ext3 / ext4 : chattr +a file.txt
dans le système de fichiers XFS :echo chattr +a | xfs_io file.txt

Et si vous voulez une fonction, j'ai déjà fait une fonction pour moi-même (je l'ai utilisée dans le fichier de service pour la journalisation des sorties), vous pouvez la changer pour votre objectif:

# This function redirect logs to file or terminal or both!
#@ USAGE: log option data
# To the file     -f file
# To the terminal -t
function log(){
        read -r data       # Read data from pipe line

        [[ -z ${indata} ]] && return 1    # Return 1 if data is null

        # Log to /var/log/messages
        logger -i -t SOFTWARE ${data}

        # While loop for traveling on the arguments
        while [[ ! -z "$*" ]]; do
                case "$1" in
                        -t)
                                # Writting data to the terminal
                                printf "%s\n" "${data}"
                                ;;
                        -f) 
                                # Writting (appending) data to given log file address
                                fileadd=$2
                                printf "%s %s\n" "[$(date +"%D %T")] ${data}" >> ${fileadd}
                                ;;
                        *)
                                ;;
                esac
                shift           # Shifting arguments
        done
}

2
Il y a quelques problèmes avec ce script. 1) Vous devriez généralement mettre des guillemets doubles autour des extensions de paramètres (ce qui commence par a $) pour éviter les problèmes de globalisation, de division de mots et d'espaces associés. L' application ShellCheck en ligne peut détecter cela et divers autres problèmes dans les scripts bash. Sur une note connexe, "$@"c'est beaucoup plus sûr que $*.
PM 2Ring

3
2) Vous devez normalement appeler la readcommande avec l' -roption pour empêcher toute barre oblique inverse dans l'entrée d'échapper au caractère suivant. 3) Il est habituel dans les scripts bash d'utiliser des minuscules pour les noms de variables de script car ALL UPPER CASE est utilisé pour les variables système. Donc, si vous utilisez des majuscules pour vos propres variables, vous confondrez les personnes qui liront votre code, et vous risquez accidentellement d'altérer une variable système que vous souhaitez utiliser plus tard dans le script. Et si vous sourcez le script, il encombrera les variables pour les commandes suivantes.
PM 2Ring

3
4) echoest pratique pour imprimer des chaînes fixes, mais il peut faire des choses inattendues avec des données arbitraires, en particulier sur les systèmes GNU. C'est beaucoup plus sûr à utiliser printf. Voir unix.stackexchange.com/questions/65803/… pour plus d'informations, en particulier la réponse de Stéphane Chazelas.
PM 2Ring

3
@PM 2Ring, Aujourd'hui, j'ai appris beaucoup de choses de vous, merci beaucoup. Je les corrigerai dès que possible.
Sepahrad Salour du

1
Beau travail, Sepahrad!
PM 2Ring

3

À utiliser teeavec l'option d'ajout:

foo | tee -a some-file
# or
tee -a some-file <<EOF
blah blah
EOF
# or 
tee -a some-file <<<"blah blah"

5
Bonne idée, +1, mais j'imagine que si l'OP s'inquiète d'oublier un >personnage, il pourrait s'inquiéter d'oublier l' -aoption!
Celada

@Celada Je suppose que oui (mais je suppose qu'une faute de frappe de manquer une touche dans un ensemble de touches répétées est plus susceptible de se produire que d'oublier une option).
muru

0

de nombreux programmes qui peuvent ouvrir des fichiers pour les écraser peuvent également les ouvrir pour les ajouter, par exemple gnu dd.

dd conv=notrunc oflag=append of=file

il peut lire stdin ou un fichier nommé dans le if= paramètre add 2>/dev/nullpour supprimer le nombre d'octets.


3
C'est une bonne idée ™ de tester votre code avant de le poster comme réponse ...
PM 2Ring

1
Recommander ddest une mauvaise idée. ddn'écrit pas de manière fiable son entrée dans sa sortie , pas lorsque l'un d'eux n'est pas un fichier standard ou un périphérique de blocage
Gilles 'SO- arrête d'être méchant'

@gilles, vous déformez les informations sur ce lien, lorsque le nombre n'est pas spécifié dd copiera toutes les données disponibles, de la même manière que le fait le chat.
Jasen

-1

Je voudrais utiliser un sed(même avec une copie de sauvegarde - voir l'extension après -i):

sed -i.bak '$ a\something' /Users/zen1/zen/pythonstudy/tree_hole

Pourquoi je ne suis pas surpris ... tu es un maniaque sed, Costas. :) (C'est un compliment, BTW)
PM 2Ring

@ PM2Ring Oui, je suis très dangereux! : P
Costas

Bien sûr, la façon dont cela fonctionne est de faire une copie du fichier, d'ajouter le nouveau texte à la fin, puis de supprimer le fichier d'origine et de renommer la copie en son nom d'origine. Cela (1) ne fonctionne pas si vous n'avez pas accès en écriture au répertoire, (2) rompt les liens et (3) est coûteux en ressources si le fichier est volumineux. C'est drôle que vous ayez évoqué le mot "dangereux"!
G-Man dit `` Réintègre Monica ''

-1

Vous pouvez toujours rechercher dans le fichier d'autres manières ...

seq 10000000 >f
{ wc -l >&2; echo new line\!; } <>f >&0; \
{ wc -l >&2; echo new line\!; } <>f >&0; \
{ wc -l >&2; echo new line\!; } <>f >&0; \
{ wc -l >&2; echo new line\!; } <>f >&0; \
{ wc -l >&2; echo new line\!; } <>f >&0; \
wc -l f; tail f

... cette séquence étrange imprime:

10000000
10000001
10000002
10000003
10000004
10000005 f
9999996
9999997
9999998
9999999
10000000
new line!
new line!
new line!
new line!
new line!

Mais c'est un peu idiot.

Un exemple plus utile pourrait ressembler à ceci:

 apnd() if    shift
        then  wc -l  >&2
              printf "$@"
        fi    <>"$1" >&0

Vous pouvez l'appeler comme:

 apnd /path/to/file          \
      "${printf_fmt_string}" \
       arbitrary list of strings

Et vous vous retrouvez avec un nombre de filelignes écrites stderrjuste avant l'action d'ajout.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.