Fondamentalement, je veux prendre comme texte d'entrée d'un fichier, supprimer une ligne de ce fichier et renvoyer la sortie dans le même fichier. Quelque chose dans ce sens si cela le rend plus clair.
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
cependant, lorsque je fais cela, je me retrouve avec un fichier vierge. Des pensées?
Vous ne pouvez pas faire cela car bash traite d'abord les redirections, puis exécute la commande. Donc, au moment où grep regarde file_name, il est déjà vide. Vous pouvez cependant utiliser un fichier temporaire.
#!/bin/sh
tmpfile=$(mktemp)
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
cat ${tmpfile} > file_name
rm -f ${tmpfile}
comme ça, pensez à utiliser mktemp
pour créer le tmpfile mais notez que ce n'est pas POSIX.
Utilisez éponge pour ce type de tâches. Sa partie de moreutils.
Essayez cette commande:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
Utilisez plutôt sed:
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
essayez ce simple
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
Votre fichier ne sera pas vierge cette fois :) et votre sortie est également imprimée sur votre terminal.
Vous ne pouvez pas utiliser l'opérateur de redirection (>
ou >>
) dans le même fichier, car il a une priorité plus élevée et il créera/tronquera le fichier avant même que la commande soit invoquée. Pour éviter cela, vous devez utiliser les outils appropriés tels que tee
, sponge
, sed -i
ou tout autre outil pouvant écrire des résultats dans le fichier (par exemple sort file -o file
).
Fondamentalement, la redirection de l'entrée vers le même fichier d'origine n'a pas de sens et vous devez utiliser des éditeurs sur place appropriés pour cela, par exemple l'éditeur Ex (qui fait partie de Vim):
ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
où:
'+cmd'
/-c
- exécutez n'importe quelle commande Ex/Vimg/pattern/d
- supprime les lignes correspondant à un modèle à l'aide de global (help :g
)-s
- mode silencieux (man ex
)-c wq
- exécuter :write
et :quit
commandesVous pouvez utiliser sed
pour obtenir la même chose (comme déjà indiqué dans d'autres réponses), cependant en place (-i
) est une extension FreeBSD non standard (peut fonctionner différemment entre Unix/Linux) et, fondamentalement, c'est un s tream ed itor, pas un éditeur de fichiers. Voir: Le mode Ex a-t-il une utilité pratique?
Une alternative liner - définissez le contenu du fichier comme variable:
VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
Puisque cette question est le meilleur résultat dans les moteurs de recherche, voici un one-liner de https://serverfault.com/a/547331 qui utilise un sous-shell au lieu de sponge
(qui souvent ne fait pas partie d'une installation Vanilla comme OS X):
echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name
Ou le cas général:
echo "$(cat file_name)" > file_name
Testez à partir de https://askubuntu.com/a/752451 :
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do echo "$(cat file_uniquely_named.txt)" > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Devrait imprimer:
hello
world
En appelant cat file_uniquely_named.txt > file_uniquely_named.txt
dans le shell actuel:
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Imprime une chaîne vide.
Je n'ai pas testé cela sur des fichiers volumineux (probablement plus de 2 ou 4 Go).
J'ai emprunté cette réponse à Hart Simha et kos .
Vous pouvez utiliser Slurp avec POSIX Awk:
!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
q = q ? q RS $0 : $0
}
END {
print q > ARGV[1]
}
Vous pouvez le faire en utilisant substitution de processus .
C'est un peu un hack bien que bash ouvre tous les canaux de manière asynchrone et nous devons contourner cela en utilisant sleep
donc YMMV.
Dans votre exemple:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
>(sleep 1 && cat > file_name)
crée un fichier temporaire qui reçoit la sortie de grepsleep 1
Attend une seconde pour donner à grep le temps d'analyser le fichier d'entréecat > file_name
écrit la sortieIl y a aussi ed
(comme alternative à sed -i
):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq | ed -s file_name
Essaye ça
echo -e "AAA\nBBB\nCCC" > testfile
cat testfile
AAA
BBB
CCC
echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC
J'utilise habituellement le programme tee pour ce faire:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
Il crée et supprime un fichier temporaire par lui-même.
Ce qui suit accomplira la même chose que sponge
, sans nécessiter moreutils
:
shuf --output=file --random-source=/dev/zero
Le --random-source=/dev/zero
partie astuces shuf
pour faire son travail sans faire de mélange du tout, donc il tamponnera votre entrée sans la modifier.
Cependant, il est vrai que l'utilisation d'un fichier temporaire est préférable, pour des raisons de performances. Donc, voici une fonction que j'ai écrite qui fera cela pour vous de manière généralisée:
# Pipes a file into a command, and pipes the output of that command
# back into the same file, ensuring that the file is not truncated.
# Parameters:
# $1: the file.
# $2: the command. (With $3... being its arguments.)
# See https://stackoverflow.com/a/55655338/773113
function siphon
{
local tmp=$(mktemp)
local file="$1"
shift
$* < "$file" > "$tmp"
mv "$tmp" "$file"
}