J'ai un dossier qui a plusieurs sous-dossiers et sous-dossiers. Je souhaite imprimer le contenu d'un fichier nommé result.txt
, présent dans de nombreux sous-dossiers ou sous-dossiers dans un fichier CSV accompagné du nom du sous-dossier.
Cela signifie que si les fichiers nommés result.txt
sont dans
abc/def/result.txt
efg/result.txt
Ensuite, j'ai besoin d'un fichier csv qui devrait avoir
1. abc content of its result.txt
2. efg content of its result.txt
etc.
J'ai commencé avec la commande find
suivante
find . -iname 'result.txt' "a portion of path" "content">final.csv
Comment dois-je procéder à partir d'ici?
Remarque: (8 décembre 2017) Bien que les solutions ci-dessous affichent le contenu correctement sur le terminal, aucune d'entre elles ne fonctionne lorsque j'ajoute> final.csv. Comme déjà mentionné, mon résultat.txt contient des mutilines. Le contenu de un résultat en particulier est renvoyé dans différentes cellules plutôt que dans une seule cellule. Des suggestions?
Je pense que find
est le bon choix:
find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(cat $0)"' {} \;
$ echo r1 >a/b/result.txt
$ echo r2 >c/result.txt
$ tree
.
├── a
│ └── b
│ └── result.txt
└── c
└── result.txt
$ find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(cat $0)"' {} \;
a,r1
c,r2
Cette commande find
recherche dans tous les fichiers situés dans ou sous le répertoire en cours du nom result.txt
et exec
active la commande printf
dans un sous-shell bash
. La commande printf
affiche le nom du sous-répertoire, une virgule et le contenu du fichier suivi d'un \n
ewline. Si vous souhaitez écrire cette sortie dans un fichier, ajoutez simplement, par exemple, >final.csv
à la commande.
est l’approche -printf
suggérée par steeldriver :
$ find */ -name 'result.txt' -printf '%H,' -exec cat {} \;
a/,r1
c/,r2
Ceci imprime une barre oblique supplémentaire dans la première colonne que vous pouvez facilement supprimer en dirigeant la sortie, par ex. sed 's|/,|,|'
.
result.txt
contenu dans une cellulePour remplacer les caractères de nouvelle ligne par ex. les espaces ne font que remplacer cat
par sed ":a;N;\$!ba;s/\n/ /g"
dans l'une des commandes ci-dessus, par exemple.
$ find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(sed ":a;N;\$!ba;s/\n/ /g" $0)"' {} \;
a,r1 r1
c,r2
Si vous souhaitez utiliser une autre chaîne comme délimiteur, remplacez la partie / /
par /your_delimiter/
, mais conservez les barres obliques.
Eh bien, voici un moyen (maintenant modifié pour transformer les sauts de ligne en espaces, grâce à cette réponse sur Stack Overflow ):
shopt -s globstar
n=0; for i in **/result.txt; do sed -e ":l;N;\$!bl;s/\n/ /g; s/.*/$((++n))\. "${i%%/*}"\t&/" "$i"; done
Vous pouvez ajouter une redirection pour écrire dans un fichier
n=0; for i in **/result.txt; do sed ":l;N;\$!bl;s/\n/ /g; s/.*/$((++n))\. "${i%%/*}"\t&/" "$i"; done > outfile
n=0
définir une variable à incrémentershopt -s globstar
Activez la suppression récursive avec **
pour rechercher tous les fichiers dans les répertoires situés en dessous de celui-ci (désélectionnez-le avec shopt -u globstar
, ou quittez le shell et démarrez-en un nouveau).:l
définir une étiquette pour cette actionN
lit deux lignes dans l'espace du modèle (cela nous permet d'utiliser \n
)\$!
pas s'il s'agit de la dernière ligne du fichier ... nous devons échapper à $
car la commande entière est double guillemet pour que le shell puisse se développer $i
etc. Mais ce $
doit être transmis intact à sed
, où il signifie "la dernière ligne du fichier". Je recommande d'utiliser guillemets simples pour sed
les scripts, sauf si vous devez y passer des variables Shell.bl
... branche à l'étiquette (recommencez)s/old/new
remplace old
par new
s/\n/ /g
pour tous les caractères de nouvelle ligne de l'espace modèle (tous sauf le dernier), remplacez la nouvelle ligne par un espace.*
un nombre quelconque de caractères (quelque chose dans le fichier)$((++n))
incrémenter n
à chaque itération de la boucle\.
point littéral (les virgules ne sont pas traitées spécialement par sed
; elles seront imprimées littéralement)"${i%%/*}"
le nom du premier sous-répertoire du répertoire actuel dans le chemin du fichier que nous traitons (effacez tous les caractères après le premier /
)&
le motif correspondant dans la section de recherche (tout ce qui est dans le fichier)--
n'interprète pas -
dans les arguments suivants en tant qu'indicateurs d'options. Cela évite que les noms de fichiers commençant par -
soient interprétés comme des options. Ceci est inutile dans ce cas particulier, car nous recherchons explicitement result.txt
et seuls les fichiers portant ce nom exact seront transmis à la boucle. Cependant, je l'ai inclus, au cas où quelqu'un aurait besoin de réutiliser ce script avec un glob.Voici une version plus lisible, qui est également plus portable (devrait fonctionner dans toutes les versions de sed
) car elle utilise des nouvelles lignes au lieu de ;
pour séparer les commandes:
#!/bin/bash
shopt -s globstar
n=0
for i in **/result.txt; do
sed ":l
N
\$!bl
s/\n/ /g
s/.*/$((++n))\.,"${i%%/*}",&/" -- "$i"
done > outfile
#!/bin/bash
# If $1 is not given, find will assume cwd
print_file(){
local inputfile="$1"
while IFS= read -r line || [ -n "$line" ];do
printf "%s\\" "$line"
done < "$inputfile"
}
get_file_info(){
local filepath="$1"
counter=$((counter+1))
parent=${filepath%/*}
if [ "$parent" = "$filepath" ]; then
parent="."
fi
printf "%d,%s," "$counter" "$parent"
}
main(){
if [ -z "$1" ];then
set "."
fi
find "$1" -type f -name "result.txt" -print0 |
while IFS= read -r -d '' path
do
get_file_info "$path"
print_file "$path"
printf "\n"
done
}
main "$@"
La façon dont cela fonctionne est que vous devriez enregistrer ceci en tant que fichier, par exemple results2csv.sh
, rendre exécutable avec chmod +x
et l'exécuter en donnant le chemin complet du script ou en le plaçant dans le dossier ~/bin
, lancez source ~/.bashrc
et appelez le script par son nom.
Voici comment ce script fonctionne:
$ ./result2csv.sh things
1,things/thing2,to be or not to be\that's Boolean logic\
2,things/thing1,one potato\two potato\
Donnez au script le répertoire le plus haut, et il passera par les sous-répertoires pour trouver les fichiers et affichera le chemin d'accès au fichier conformément à la façon dont vous avez spécifié le répertoire le plus haut. Ainsi, par exemple, si vous spécifiez ./things
en haut de la liste, la première ligne aurait pour résultat ./thing/things2
comme chemin d'accès au fichier. Les nouvelles lignes sont remplacées par des barres obliques inverses pour afficher le contenu du fichier. Notez que cela supposera également le répertoire de travail actuel "." si le répertoire n'est pas spécifié.
$ cd things
$ ../result2csv.sh
1,./thing2,to be or not to be\that's Boolean logic\
2,./thing1,one potato\two potato\
Il ne vous reste plus qu'à appeler results2csv.sh directory > output.csv
pour exporter les données dans un fichier, et vous avez terminé.