J'essaie de créer un motif pour une dizaine de fichiers .tar.gz, mais c'est très lent
j'utilise
tar -ztf file.tar.gz | while read FILENAME
do
if tar -zxf file.tar.gz "$FILENAME" -O | grep "string" > /dev/null
then
echo "$FILENAME contains string"
fi
done
Si vous avez zgrep
vous pouvez utiliser
zgrep -a string file.tar.gz
Vous pouvez utiliser le --to-command
option pour diriger les fichiers vers un script arbitraire. Grâce à cela, vous pouvez traiter l’archive en une seule passe (et sans fichier temporaire). Voir aussi cette question , et le manuel . Armé des informations ci-dessus, vous pouvez essayer quelque chose comme:
$ tar xf file.tar.gz --to-command "awk '/bar/ { print ENVIRON[\"TAR_FILENAME\"]; exit }'"
bfe2/.bferc
bfe2/CHANGELOG
bfe2/README.bferc
Je sais que cette question a 4 ans, mais j'ai deux options différentes:
tar --to-command grep
La ligne suivante va regarder dans example.tgz
pour PATTERN
. Ceci est similaire à l'exemple de @ Jester, mais je ne parviens pas à faire correspondre sa correspondance de motif.
tar xzf example.tgz --to-command 'grep --label="$TAR_FILENAME" -H PATTERN ; true'
tar -tzf
La deuxième option utilise tar -tzf
pour lister les fichiers, puis parcourez-les avec grep
. Vous pouvez créer une fonction pour l'utiliser encore et encore:
targrep () {
for i in $(tar -tzf "$1"); do
results=$(tar -Oxzf "$1" "$i" | grep --label="$i" -H "$2")
echo "$results"
done
}
Usage:
targrep example.tar.gz "pattern"
Si cela est vraiment lent, je suppose que vous avez affaire à un fichier d'archive volumineux. Il va le décompresser une fois pour extraire la liste de fichiers, puis le décompresser N fois - où N est le nombre de fichiers de l'archive - pour le grep. En plus de la décompression, il va falloir numériser un peu chaque fois l'archive pour extraire chaque fichier. Un des plus gros inconvénients de tar
est qu’il n’ya pas de table des matières au début. Il n'existe aucun moyen efficace d'obtenir des informations sur tous les fichiers de l'archive et de ne lire que cette partie du fichier. Il doit essentiellement lire tout le fichier jusqu'à la chose que vous extrayez à chaque fois; il ne peut pas simplement aller tout de suite à l'emplacement d'un nom de fichier.
La chose la plus simple que vous puissiez faire pour accélérer le processus consiste à décompresser d’abord le fichier (gunzip file.tar.gz
) et ensuite travailler sur le .tar
fichier. Cela pourrait aider assez par lui-même. Il restera cependant à parcourir l'archive entière N fois.
Si vous voulez vraiment que cela soit efficace, votre seule option est d'extraire complètement tout l'archive avant de la traiter. Puisque votre problème est la rapidité, je suppose que c'est un fichier géant que vous ne voulez pas extraire en premier, mais si vous le pouvez, cela accélérera beaucoup les choses:
tar zxf file.tar.gz
for f in hopefullySomeSubdir/*; do
grep -l "string" $f
done
Notez que grep -l
affiche le nom de tout fichier correspondant, se ferme après la première correspondance et reste muet s'il n'y a pas de correspondance. Cela seul accélérera la partie grepping de votre commande, donc même si vous n'avez pas assez d'espace pour extraire toute l'archive, grep -l
aidera. Si les fichiers sont énormes, cela aidera beaucoup.
Pour commencer, vous pouvez démarrer plusieurs processus:
tar -ztf file.tar.gz | while read FILENAME
do
(if tar -zxf file.tar.gz "$FILENAME" -O | grep -l "string"
then
echo "$FILENAME contains string"
fi) &
done
Le ( ... ) &
crée un nouveau processus détaché (read: le shell parent n'attend pas l'enfant).
Après cela, vous devez optimiser l'extraction de votre archive. La lecture n’est pas un problème, car le système d’exploitation aurait déjà déjà mis en cache l’accès au fichier. Cependant, tar doit décompresser l'archive à chaque exécution de la boucle, ce qui peut être lent. Décompresser l'archive une fois et parcourir le résultat peuvent aider ici:
local tempPath=`tempfile`
mkdir $tempPath && tar -zxf file.tar.gz -C $tempPath &&
find $tempPath -type f | while read FILENAME
do
(if grep -l "string" "$FILENAME"
then
echo "$FILENAME contains string"
fi) &
done && rm -r $tempPath
find
est utilisé ici pour obtenir une liste des fichiers dans le répertoire cible de tar
sur lequel nous effectuons une nouvelle itération, pour chaque fichier cherchant une chaîne.
Edit: Utilisez grep -l
pour accélérer les choses, comme l'a souligné Jim. De man grep
:
-l, --files-with-matches
Suppress normal output; instead print the name of each input file from which output would
normally have been printed. The scanning will stop on the first match. (-l is specified
by POSIX.)
Cette option est vraiment viable: zcat log.tar.gz | grep -a -i "chaîne"
Ceci imprimera toute la ligne qui correspond à votre motif. zgrep ne donne pas vraiment une sortie utile.
$ zgrep -i 'CDF_FEED' FeedService.log.1.05-31-2019-150003.tar.gz | more
Binary file (standard input) matches
$ zcat FeedService.log.1.05-31-2019-150003.tar.gz | grep -ai 'CDF_FEED'
2019-05-30 19:20:14.568 ERROR 281 --- [http-nio-8007-exec-360] DrupalFeedService : CDF_FEED_SERVICE::CLASSIFICATION_ERROR:408: Classification failed even after maximum retries for url : abcd.html
Tout le code ci-dessus était vraiment utile, mais rien de tout cela ne répondait parfaitement à mon propre besoin: grep
all *.tar.gz
fichiers dans le répertoire en cours pour trouver un modèle spécifié en tant qu’argument dans un script réutilisable afin de le générer:
C'est ce que j'espérais vraiment que zgrep
puisse faire pour moi et il ne le peut tout simplement pas.
Voici ma solution:
pattern=$1
for f in *.tar.gz; do
echo "$f:"
tar -xzf "$f" --to-command 'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true";
done
Vous pouvez également remplacer la ligne tar
par ce qui suit si vous souhaitez vérifier que toutes les variables se développent correctement avec une instruction de base echo
:
tar -xzf "$f" --to-command 'echo "f:`basename $TAR_FILENAME` s:'"$pattern\""
Laissez-moi vous expliquer ce qui se passe. Espérons que la boucle for
et le echo
du nom de fichier de l’archive en question sont évidents.
tar -xzf
: x
extraire, z
filtrer par gzip, f
en fonction du fichier d’archive suivant ...
"$f"
: Le fichier d’archive fourni par la boucle for (comme ce que vous obtiendriez en faisant un ls
) entre guillemets pour permettre à la variable de se développer et de s’assurer que le script n’est cassé par aucun fichier. noms avec espaces, etc.
--to-command
: Passez le résultat de la commande tar à une autre commande plutôt que d'extraire des fichiers dans le système de fichiers. Tout ce qui suit après spécifie ce qu'est la commande (grep
) et quels arguments nous passons à cette commande.
Décomposons cette partie par elle-même, car c'est la "sauce secrète" ici.
'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true"
Tout d’abord, nous utilisons un guillemet simple pour démarrer ce bloc de sorte que la sous-commande exécutée (basename $TAR_FILENAME
) est pas immédiatement développé/résolu. Plus sur cela dans un instant.
grep
: La commande à exécuter sur les fichiers extraits (pas réellement)
--label=
: Le libellé à ajouter au début des résultats, dont la valeur est placée entre guillemets car nous do voulons que la commande grep
résolve le $TAR_FILENAME
variable d’environnement transmise par la commande tar
.
basename $TAR_FILENAME
: S'exécute en tant que commande (entourée de backticks), supprime le chemin du répertoire et affiche uniquement le nom du fichier.
-Hin
: H
Affiche le nom du fichier (fourni par l’étiquette), i
Recherche insensible à la casse, n
Affiche le numéro de ligne de la correspondance
Ensuite, nous "finissons" la première partie de la chaîne de commande par un guillemet simple et commençons la partie suivante par un guillemet double de sorte que le $pattern
, passé en tant que premier argument, peut être résolu.
Réaliser les citations que je devais utiliser était la partie qui m'avait le plus retardée. J'espère que tout cela a du sens pour vous et aide quelqu'un d'autre. De plus, j'espère pouvoir le trouver dans une année où j'en aurai de nouveau besoin (et j'ai déjà oublié le script que j'ai déjà conçu pour cela!)
Et cela fait un peu de semaines que j'ai écrit ce qui précède et c'est toujours très utile ... mais ce n'était pas assez bon car les fichiers se sont accumulés et la recherche d'objets a été compliquée. J'avais besoin d'un moyen de limiter ce que j'ai regardé par la date du fichier (en regardant uniquement les fichiers plus récents). Alors voici ce code. J'espère que c'est assez explicite.
if [ -z "$1" ]; then
echo "Look within all tar.gz files for a string pattern, optionally only in recent files"
echo "Usage: targrep <string to search for> [start date]"
fi
pattern=$1
startdatein=$2
startdate=$(date -d "$startdatein" +%s)
for f in *.tar.gz; do
filedate=$(date -r "$f" +%s)
if [[ -z "$startdatein" ]] || [[ $filedate -ge $startdate ]]; then
echo "$f:"
tar -xzf "$f" --to-command 'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true"
fi
done
Et je ne peux pas m'empêcher de peaufiner cette chose. J'ai ajouté un argument pour filtrer par le nom des fichiers de sortie dans le fichier tar. Les jokers fonctionnent aussi.
Usage:
targrep.sh [-d <start date>] [-f <filename to include>] <string to search for>
Exemple:
targrep.sh -d "1/1/2019" -f "*vehicle_models.csv" ford
while getopts "d:f:" opt; do
case $opt in
d) startdatein=$OPTARG;;
f) targetfile=$OPTARG;;
esac
done
shift "$((OPTIND-1))" # Discard options and bring forward remaining arguments
pattern=$1
echo "Searching for: $pattern"
if [[ -n $targetfile ]]; then
echo "in filenames: $targetfile"
fi
startdate=$(date -d "$startdatein" +%s)
for f in *.tar.gz; do
filedate=$(date -r "$f" +%s)
if [[ -z "$startdatein" ]] || [[ $filedate -ge $startdate ]]; then
echo "$f:"
if [[ -z "$targetfile" ]]; then
tar -xzf "$f" --to-command 'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true"
else
tar -xzf "$f" --no-anchored "$targetfile" --to-command 'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true"
fi
fi
done