Accidentellement utilisé la redirection de sortie> au lieu d'un tuyau |

21

Il y a un mois, j'ai écrit un script Python pour mapper les adresses MAC et IP de stdin. Et il y a deux jours, je m'en souvenais et je filtrais la sortie de tcpdumpmais ça a mal tourné à cause d'une faute de frappe. j'ai tapé $

tcpdump -ne > ./mac_ip.py

et la sortie n'est rien. Mais la sortie devrait être "Inconnue" si elle ne peut pas analyser l'entrée, donc j'ai fait cat ./mac_ip.pyet trouvé toutes les tcpdumpdonnées au lieu du programme. Ensuite, j'ai réalisé que je devrais utiliser

tcpdump -ne | ./mac_ip.py

Existe-t-il un moyen de récupérer mon programme? Quoi qu'il en soit, je peux réécrire mon programme, mais si cela se reproduit avec un programme plus important, je devrais pouvoir faire quelque chose. OU existe-t-il un moyen de dire à la redirection de sortie de vérifier le fichier et d'avertir s'il s'agit d'un exécutable?

Bharath Teja
la source
18
Vous pouvez récupérer votre programme à partir de la dernière sauvegarde avant votre remplacement, sinon non. BTW dans le shell que vous pouvez spécifier set -o noglobberet bash ne redirigera plus vers les fichiers existants. Voir ici pour plus de détails: cyberciti.biz/tips/howto-keep-file-safe-from-overwriting.html
eckes
12
Vous ne devriez pas avoir la permission d'écrire sur des exécutables importants ...
Hagen von Eitzen
20
@eckesset -o noclobber
GnP
38
@HagenvonEitzen Je déteste les conseils comme celui-ci, comme si vous aviez défini la propriété et les autorisations appropriées sur tous les scripts shell et python uniques que vous avez déjà écrits avant de les exécuter (et, bien sûr, revenez brièvement si vous devez les modifier ). C'est à peine plus significatif que «Vous ne devriez pas taper >quand vous voulez dire |. N'oubliez pas la réalité.
Jason C
30
Les dépôts Git sont bon marché. Engagez tout votre code, peu importe sa taille et sa signification, puis une erreur comme celle-ci est une solution rapide et facile.
casey

Réponses:

22

Malheureusement, je pense que vous devrez le réécrire. (Si vous avez des sauvegardes, c'est le moment de les retirer. Sinon, je vous recommande fortement de configurer un régime de sauvegarde pour l'avenir. Beaucoup d'options disponibles, mais hors sujet pour cette réponse.)

Je trouve que mettre des exécutables dans un répertoire séparé et ajouter ce répertoire au PATHest utile. De cette façon, je n'ai pas besoin de référencer les exécutables par un chemin explicite. Mon répertoire de programmes préféré pour les scripts personnels (privés) est "$HOME"/binet il peut être ajouté au chemin de recherche du programme avec PATH="$HOME/bin:$PATH". En général, cela serait ajouté aux scripts de démarrage du shell .bash_profileet / ou .bashrc.

Enfin, rien ne vous empêche de supprimer l'autorisation d'écriture pour vous-même sur tous les programmes exécutables:

touch some_executable.py
chmod a+x,a-w some_executable.py    # chmod 555, if you prefer

ls -l some_executable.py
-r-xr-xr-x+ 1 roaima roaima 0 Jun 25 18:33 some_executable.py

echo "The hunting of the Snark" > ./some_executable.py
-bash: ./some_executable.py: Permission denied
roaima
la source
2
/usr/local/binest l'emplacement standard pour les exécutables et scripts créés par l'utilisateur
gardenhead
4
@gardenhead Cela dépend de la configuration du système. /usr/localest destiné à des choses spécifiques à l'hôte (par opposition à un répertoire partagé entre des hôtes via un montage réseau), et peut ou non être accessible en écriture par des utilisateurs non root.
chepner
4
@gardenhead c'est certainement un emplacement standard. J'utilise /use/local/binpour les scripts et les programmes installés localement qui sont susceptibles d'être utilisés par plusieurs comptes d'utilisateurs, et $HOME/binpour des choses personnelles à un seul utilisateur. Il y a de la valeur dans les deux.
roaima
1
Notez que Fedora semble essayer de pousser en utilisant$HOME/.local/bin
Zan Lynx
1
@Zan eeeww! Sérieusement, merci. On dirait que RH essaie de tout pousser ~/.localcar c'est encore un autre élément déplacé de son endroit "traditionnel".
roaima
38

Pour éviter que les fichiers existants ne soient écrasés par la redirection, >utilisez l' noclobberoption dans bashou n'importe quel shell de type POSIX (également (t)cshlà où la fonctionnalité a réellement commencé, bien que vous le fassiez à la set noclobberplace de set -o noclobber/ set -Clà). Ensuite, si vous devez forcer le remplacement d'un fichier, utilisez l' >|opérateur de redirection ( >!in (t)csh).

Exemple:

$ echo abc > file
$ set -o noclobber
$ echo xyz > file
bash: file: cannot overwrite existing file
$ echo xyz >| file
$ cat file
xyz

BTW, vous pouvez vérifier les paramètres actuels avec set -o:

$ set -o
...
monitor         on
noclobber       on
noexec          off
...
jimmij
la source
Bien que cela réponde parfaitement à la question, je ne le recommanderais pas. 1. Taper >|au lieu de |n'est pas beaucoup moins probable que de taper >. 2. Il est facile et hautement recommandé de faire des sauvegardes (un éditeur digne de ce nom peut enregistrer la dernière version; il y en a cron, etc.). 3. Chaque morceau de code doit être placé sous contrôle de version, même de petits scripts. YMMV.
maaartinus
2
@maaartinus allez, 1) taper deux caractères séparés au lieu d'un est clairement moins probable. 2) Évidemment, les sauvegardes sont essentielles, personne n'a conseillé au PO de ne pas faire de sauvegardes, cette réponse ne suggère en aucun cas de ne pas avoir de sauvegardes, et les sauvegardes de l'éditeur supposent que vous avez édité le fichier dans un éditeur. 3) Encore une fois, vous ne pensez qu'au code que l'OP a écrit, comme dans cet exemple particulier, mais la question et cette réponse s'appliquent à tout fichier sur la machine, y compris les exécutables système.
terdon
8

Je conseille fortement d'avoir les scripts importants sous un dépôt git , synchronisés à distance (une plate-forme auto-hébergée de fantaisie fera l'affaire), comme le dit le commentaire de @ casey.

De cette façon, vous êtes protégé contre les mauvaises erreurs humaines comme le retour du fichier à l'état de travail précédent et le réexécutez.

RcrdBrt
la source
4

Le fichier est-il récupérable?

Réponse courte: Pas habituellement.

@Mark Plotnick souligne dans les commentaires que vous pouvez récupérer des .pyfichiers en .pycutilisant Uncompyle . Cela devrait être parfait pour votre situation.

En général, cependant, c'est beaucoup plus difficile. Théoriquement, vous pouvez utiliser des outils de criminalistique pour restaurer des fichiers. Le plus simple que j'ai utilisé est probablement testdisk(alias "PhotoRec"). Cela ne fonctionne que parfois et c'est un processus lent. Cela n'en vaut généralement pas la peine, alors oui, c'est possible , mais la vraie réponse est «non».

Peut > être modifié pour ne pas écraser les exécutables?

Non. Il n'existe aucun moyen standard de dire au shell de ne jamais rediriger uniquement pour les fichiers marqués comme exécutables. Il y a "noclobber" qui empêchera la redirection vers des fichiers existants, exécutables ou non, mais voyez mes commentaires à ce sujet ci-dessous.

Que faire à l'avenir?

  1. Cela peut sembler idiot, mais pour éviter de futures erreurs, vous n'avez probablement rien à faire. Je parie que vous avez déjà appris cette leçon.

    J'utilise et enseigne Unix depuis très longtemps et même si les gens font souvent cette erreur une fois, ils la répètent rarement. Pourquoi pas? Probablement pour la même raison qu'une personne expérimentée avec des couteaux ne se coupe pas: les humains sont bons à apprendre. Finalement, faire la bonne chose devient une seconde nature.

  2. Utilisez un éditeur de texte qui effectue des sauvegardes pour vous. Par exemple, si vous utilisez emacs, la version précédente de votre programme est enregistrée dans mac_ip.py ~. D'autres éditeurs peuvent être configurés pour fonctionner de manière similaire (par exemple, "définir la sauvegarde" dans .nanorc). Pour les éditeurs qui ne prennent pas en charge les sauvegardes automatiques, vous pouvez créer une fonction simpliste dans votre .bashrc:

    myeditor() { cp -p "$1" "$1~";  editor "$1"; }
    
  3. Faites-en facilement des copies. Par exemple, dans le répertoire du projet sur lequel vous travaillez, vous pourriez avoir un Makefile avec une cible comme celle-ci:

    # Use `make tar` to backup all files in this directory.
    # Tar filename will be ../<currentdirectory>-<date>.tar.gz 
    DIRNAME = $(shell basename `pwd`)
    TIMESTAMP = $(shell date +%s)
    tar:
        @echo "[Tarring up ${DIRNAME}.tar.gz]"
        (cd .. ; tar -zcvf "${DIRNAME}-${TIMESTAMP}.tar.gz" "${DIRNAME}")
    

    (Remarque: stackexchange attribue de manière erronée les TAB ci-dessus à 4 espaces.)

  4. De même, vous pouvez créer une cible Makefile qui fait un rsyncà un hôte Unix distant que vous avezssh accès. (Utilisez-le ssh-copy-idpour que votre mot de passe ne vous soit pas demandé à plusieurs reprises.)

  5. Utilisez git. Il existe de nombreux excellents didacticiels pour commencer. Essayez man gittutorial, man gittutorial-2etman giteveryday . La configuration de votre propre référentiel git n'est pas difficile, mais vous pouvez également créer un référentiel distant sans frais sur github.com

  6. Si les solutions ci-dessus sont trop lourdes, vous pouvez enregistrer de petits scripts sur gist.github.com . Bien qu'il soit possible de coller ou de télécharger à partir d'un navigateur Web, je recommande d'utiliser un interface gist en ligne de commande pour rendre les choses super faciles.

Je déconseille fortement d'utiliser "noclobber".

Oui, si vous le souhaitez, vous pouvez le faire set -o noclobber, vous obtiendrez des messages d'erreur chaque fois que vous essayez d'écraser un fichier existant. C'est une mauvaise idée, à mon avis.*

Il fait fonctionner le shell d'une manière non standard sans aucune indication visible s'il est activé. Vous devez utiliser une syntaxe différente pour faire des choses normales. Pire encore, si vous vous habituez au noclobber, un jour vous utiliserez une autre machine Unix sans noclobber et ce genre d'accident pourrait se reproduire.

Comme vous le savez probablement, le shell Unix a été conçu pour être un outil pointu pour les experts. Il est rapide à utiliser et ne vous gênera pas - et il vous coupera si vous oubliez quelle extrémité est pointue. Mais, plus vous l'utilisez, plus je pense que vous apprécierez que cela puisse être une bonne chose.


* Note de bas de page: prenez peut-être mes opinions avec un grain de sel. Je suis aussi le genre de personne qui pense que les roues d'entraînement à vélo sont une mauvaise idée.

hackerb9
la source
J'ai également enseigné à Unix pendant un certain temps. Beaucoup de mes étudiants n'ont jamais appris à apprécier la simplicité directe d'Unix; Je leur dis qu'ils ne sont pas seuls et qu'ils peuvent au moins encore apprendre tout en compatissant avec le manuel Unix Hater, qui trace une partie du champ de mines pour eux. simson.net/ref/ugh.pdf
Jason
Aussi: je suis d'accord - les roues d'entraînement sur un vélo sont utiles pour quiconque apprend à conduire un tricycle.
Jason
2

Vous avez peut-être pu récupérer les données après leur première apparition si vous aviez récemment consulté ou modifié le script et qu'il était toujours dans la mémoire tampon. Sinon, vous n'avez pas de chance.

Si vous avez teeécrit pour écrire dans un fichier (ainsi que STDOUT) au lieu de >(ou tee -aau lieu de >>), vous pouvez facilement remplacertee par un alias, une fonction ou un lien symbolique vers un script qui avertit l'utilisateur si le fichier qu'il est sur le point d'écrire to est exécutable.

Ce qui suit n'est en aucun cas idéal et pourrait être amélioré de beaucoup , mais c'est un point de départ, tout comme un exemple de la façon dont cela est possible:

wee.sh:

#!/bin/bash

if [ -n "${2}" ]; then
  if [ "$(ls -l "${2}" | awk '{print $1}' | grep x)" ]; then
    echo executable
  else
    tee -a "${2}"
  fi
elif [ "$(ls -l "${1}" | awk '{print $1}' | grep x)" ]; then
  echo executable
else
  tee "${1}"
fi

... alors juste echo 'alias tee="/path/to/wee.sh"' >> ~/.bashrcou quelque chose de similaire.

Du côté positif, vous obtiendrez au moins plus de pratique et la deuxième version de votre script Python sera probablement bien meilleure que la première!

rubynorails
la source
1

Vous n'avez pas précisé si vous travaillez sur un PC ou un serveur. Si vos fichiers sont stockés sur un serveur de fichiers dédié, il y a souvent des sauvegardes automatiques («instantanés») conservées par le (OS sur le) matériel du serveur de fichiers.

Sous Linux

Le répertoire d'instantanés caché virtuel existe dans chaque répertoire de votre système de fichiers.

Essayer:

cd .snapshot   
ls -l

Si ce répertoire existe, vous avez peut-être de la chance. Vous devriez voir une série de répertoires contenant des sauvegardes stockées automatiquement à certains moments. Les noms indiquent l'heure relative dans le passé à laquelle l'instantané a été stocké. Par exemple:

hourly.0
hourly.1
hourly.2
hourly.3
hourly.4
hourly.5
nightly.0
nightly.1
nightly.2
nightly.3
nightly.4
nightly.5
nightly.6
weekly.0
weekly.1
weekly.2

Allez dans n'importe quel répertoire timepoint qui est assez ancien (avant votre erreur d'écrasement de fichier). À l'intérieur du répertoire timepoint, vous devriez voir l'état du ../..répertoire (et de tous les sous-répertoires) à partir de ce point dans le passé.

cd nightly.6
ls  # look around   
tee < mac_ip.py  # check for the correct content
cp mac_ip.py ~/safekeeping/mac_ip.py  # save the old file

Remarques:

  1. ls -an'affichera pas le .snapshotrépertoire; vous devez le nommer explicitement. Il est inséré virtuellement par le serveur de fichiers. Il n'existe pas en tant que véritable répertoire dans votre système de fichiers.
  2. Ces instantanés automatiques sont un historique évolutif. Les anciens changements finissent par tomber et sont perdus. Vous devez utiliser cette technique dès que possible après avoir réalisé que vous avez besoin d'un fichier de retour.

Sous Windows

Le répertoire de clichés masqués peut être nommé ~ snapshot et exister uniquement au niveau racine d'un lecteur donné.

Conseil

Les instantanés sont un filet de sécurité qui fonctionne la plupart du temps, mais pas à chaque fois. Je suis d'accord avec les autres recommandations pour utiliser un système de contrôle de version (tel que git) même pour des fichiers triviaux.

jskroch
la source
1

Cela a déjà été dit et je le redis. Utilisez un système de contrôle des révisions.

Les sauvegardes servent à récupérer une panne matérielle. Le contrôle des révisions est destiné à des situations comme la vôtre (et il a de nombreuses autres utilisations). Les outils de contrôle des révisions vous permettent de conserver un historique d'un fichier et de revenir à tout moment de cet historique.

Les exemples d'outils de contrôle des révisions incluent la subversion (SVN) (un peu ancienne maintenant, mais toujours bonne), mercurial (hg) et git (git) (difficile à utiliser). svn est bon pour les documents bureautiques, et d'autres um-mergables, git et hg l'ont dépassé pour la plupart des autres rôles. hg et git vous permettent de travailler hors ligne et de vous synchroniser avec un serveur distant, pour la distribution et la sauvegarde.

Lisez le contrôle de révision, puis le contrôle de révision distribué, puis essayez-les.

ctrl-alt-delor
la source
Je suis d'accord que l'utilisation du contrôle des révisions est préférable pour des situations comme la mienne, mais donner les bonnes autorisations aux fichiers est tout aussi important
Bharath Teja