Je veux trouver tous les sous-dossiers, qui contiennent un fichier de démarque avec le même nom (et l'extension .md
).
Par exemple: je souhaite rechercher les sous-dossiers suivants:
Apple/Banana/Orange #Apple/Banana/Orange/Orange.md exists
Apple/Banana #Apple/Banana/Banana.md exists
Apple/Banana/Papaya #Apple/Banana/Papaya/Papaya.md exists
- Remarque: Il peut y avoir d'autres fichiers ou sous-répertoires dans le répertoire.
Aucune suggestion?
Les solutions au problème peuvent être testées à l'aide du code suivant:
#!/usr/bin/env bash
# - goal: "Test"
# - author: Nikhil Agarwal
# - date: Wednesday, August 07, 2019
# - status: P T' (P: Prototyping, T: Tested)
# - usage: ./Test.sh
# - include:
# 1.
# - refer:
# 1. [directory - Find only those folders that contain a File with the same name as the Folder - Unix & Linux Stack Exchange](/unix/534190/find-only-those-folders-that-contain-a-file-with-the-same-name-as-the-folder)
# - formatting:
# shellcheck disable=
#clear
main() {
TestData
ExpectedOutput
TestFunction "${1:?"Please enter a test number, as the first argument, to be executed!"}"
}
TestFunction() {
echo "Test Function"
echo "============="
"Test${1}"
echo ""
}
Test1() {
echo "Description: Thor"
find . -type f -regextype egrep -regex '.*/([^/]+)/\1\.md$' | sort
echo "Observation: ${Green:=}Pass, but shows filepath instead of directory path${Normal:=}"
}
Test2() {
echo "Description: Kusalananda1"
find . -type d -exec sh -c '
dirpath=$1
set -- "$dirpath"/*.md
[ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]' sh {} \; -print | sort
echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}
Test3() {
echo "Description: Kusalananda2"
find . -type d -exec sh -c '
for dirpath do
set -- "$dirpath"/*.md
if [ -f "$dirpath/${dirpath##*/}.md" ] && [ "$#" -eq 1 ]
then
printf "%s\n" "$dirpath"
fi
done' sh {} + | sort
echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}
Test4() {
echo "Description: steeldriver1"
find . -type d -exec sh -c '[ -f "$1/${1##*/}.md" ]' find-sh {} \; -print | sort
echo "Observation: ${Green:=}Pass${Normal:=}"
}
Test5() {
echo "Description: steeldriver2"
find . -type d -exec sh -c '
for d do
[ -f "$d/${d##*/}.md" ] && printf "%s\n" "$d"
done' find-sh {} + | sort
echo "Observation: ${Green:=}Pass${Normal:=}"
}
Test6() {
echo "Description: Stéphane Chazelas"
find . -name '*.md' -print0 \
| gawk -v RS='\0' -F/ -v OFS=/ '
{filename = $NF; NF--
if ($(NF)".md" == filename) include[$0]
else exclude[$0]
}
END {for (i in include) if (!(i in exclude)) print i}'
echo "Observation: ${Red:=}Fails as it ignores B.md${Normal:=}"
}
Test7() {
echo "Description: Zach"
#shellcheck disable=2044
for fd in $(find . -type d); do
dir=${fd##*/}
if [ -f "${fd}/${dir}.md" ]; then
ls "${fd}/${dir}.md"
fi
done
echo "Observation: ${Green:=}Pass but shows filepath instead of directory${Normal:=}"
}
ExpectedOutput() {
echo "Expected Output"
echo "==============="
cat << EOT
./GeneratedTest/A
./GeneratedTest/A/AA
./GeneratedTest/B
./GeneratedTest/C/CC1
./GeneratedTest/C/CC2
EOT
}
TestData() {
rm -rf GeneratedTest
mkdir -p GeneratedTest/A/AA
touch GeneratedTest/index.md
touch GeneratedTest/A/A.md
touch GeneratedTest/A/AA/AA.md
mkdir -p GeneratedTest/B
touch GeneratedTest/B/B.md
touch GeneratedTest/B/index.md
mkdir -p GeneratedTest/C/CC1
touch GeneratedTest/C/index.md
touch GeneratedTest/C/CC1/CC1.md
mkdir -p GeneratedTest/C/CC2
touch GeneratedTest/C/CC2/CC2.md
mkdir -p GeneratedTest/C/CC3
touch GeneratedTest/C/CC3/CC.md
mkdir -p GeneratedTest/C/CC4
}
main "$@"
foo/foo.md
etfoo/bar.md
doitfoo
être inclus ou exclus?-printf
avec find, vous pouvez obtenir n'importe quelle partie du match que vous voulez, voir mon montageRéponses:
En supposant que vos fichiers portent un nom raisonnable, c'est-à-dire pas besoin de
-print0
etc. Vous pouvez le faire avec GNU find comme ceci:Production:
Si vous ne voulez que le nom du répertoire, ajoutez un
-printf
argument:Sortie lors de l'exécution sur vos données de test mises à jour:
la source
find . -type f | egrep '.*/([^/]+)/\1\.md$'
print0
.%h
dans printf est utilisé pour le type int de données à formater. Référence: chaîne de format printf - Wikipedia . Pourriez-vous expliquer cette partie? Comment est%h
utilisé ici?find
, voir la section 3.2.2.1 du manuel pour plus de détails.Sur un système GNU, vous pourriez faire quelque chose comme:
la source
zsh
solution précédente (elle a obtenu, je crois, deux des votes positifs) et n'hésitez pas à signaler les défauts qui pourraient vous avoir incité à la supprimer.Ce qui précède trouverait tous les répertoires sous le répertoire courant (y compris le répertoire courant) et exécuterait un court script shell pour chacun.
Le code shell testerait s'il y a un fichier de démarque avec le même nom que le répertoire dans le répertoire, et si c'est le seul
*.md
nom dans ce répertoire. Si un tel fichier existe et s'il s'agit du seul*.md
nom, le script shell en ligne se termine avec un état de sortie nul. Sinon, il sort avec un état de sortie non nul (échec de signalisation).Le
set -- "$dirpath"/*.md
bit définira les paramètres de position sur la liste des chemins correspondant au modèle (correspond à n'importe quel nom avec un suffixe.md
dans le répertoire). Nous pouvons ensuite utiliser$#
plus tard pour voir combien de correspondances nous en avons tirées.Si le script shell se termine avec succès,
-print
affichera le chemin d'accès au répertoire trouvé.Version légèrement plus rapide qui utilise moins d'invocations du script en ligne, mais qui ne vous permet pas d'en faire plus avec les chemins d'accès trouvés en
find
soi (le script en ligne peut cependant être développé):Les mêmes commandes mais sans se soucier s'il y a d'autres
.md
fichiers dans les répertoires:Voir également:
la source
Soit
ou
Pour éviter d'en exécuter un
sh
par fichier.Il
find-sh
s'agit d'une chaîne arbitraire qui devient le paramètre de position zéro du shell$0
- en faire quelque chose de mémorable peut aider au débogage au cas où le shell rencontrerait des erreurs (d'autres peuvent suggérer d'utiliser plainsh
ou même_
comme paramètre par défaut "skip").la source
Voici la mienne. J'ai ajouté quelques répertoires et fichiers supplémentaires pour vérifier. J'étais aussi ennuyé, alors j'ai ajouté la dernière heure modifiée et MD5. Vous cherchez peut-être des doublons.
la source
Cela nécessiterait un peu de logique.
Vous pouvez également l'adapter pour qu'il tienne dans un seul revêtement en utilisant des blocs de code.
EDIT: Bash est dur.
basedir
n'est pas une commande,dirname
ne fait pas ce que je pensais, alors allons-y avec l'expansion des paramètres.la source
dirname
est la commande que vous recherchez, et les affectations ne peuvent pas avoir d’espaces autour du=
.