Quiero encontrar todas las subcarpetas, que contienen un archivo de descuento con el mismo nombre (y extensión .md
).
Por ejemplo: Quiero buscar las siguientes subcarpetas:
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
- Nota: Puede haber otros archivos o subdirectorios en el directorio.
¿Alguna sugerencia?
Las soluciones al problema se pueden probar con el siguiente código:
#!/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
yfoo/bar.md
debefoo
ser incluido o excluido?-printf
con find, puedes obtener cualquier parte de la partida que quieras, mira mi ediciónRespuestas:
Asumiendo que sus archivos tienen un nombre sensato, es decir, no es necesario,
-print0
etc. Puede hacer esto con GNU find así:Salida:
Si solo desea el nombre del directorio, agregue un
-printf
argumento:Salida cuando se ejecuta en sus datos de prueba actualizados:
fuente
find . -type f | egrep '.*/([^/]+)/\1\.md$'
print0
.%h
en printf se usa para el tipo de datos int a formatear. Referencia: cadena de formato printf - Wikipedia . ¿Podría explicar esa parte? ¿Cómo se%h
usa aquí?find
, consulte la sección 3.2.2.1 en el manual para obtener más detalles.En un sistema GNU, podría hacer algo como:
fuente
zsh
solución anterior (creo que recibió dos de los votos a favor), y siéntase libre de señalar cualquier falla que pueda haberlo llevado a eliminarla.Lo anterior encontraría todos los directorios debajo del directorio actual (incluido el directorio actual) y ejecutaría un script de shell corto para cada uno.
El código de shell probaría si hay un archivo de descuento con el mismo nombre que el directorio dentro del directorio, y si este es el único
*.md
nombre en ese directorio. Si existe dicho archivo y es el único*.md
nombre, el script de shell en línea sale con un estado de salida cero. De lo contrario, sale con un estado de salida distinto de cero (falla de señalización).El
set -- "$dirpath"/*.md
bit establecerá los parámetros posicionales en la lista de nombres de ruta que coinciden con el patrón (coincide con cualquier nombre con un sufijo.md
en el directorio). Luego podemos usar$#
más tarde para ver cuántas coincidencias obtuvimos de esto.Si el script de shell se cierra correctamente,
-print
imprimirá la ruta al directorio encontrado.Versión un poco más rápida que utiliza menos invocaciones del script en línea, pero eso no le permite hacer más con los nombres de ruta encontrados en
find
sí mismos (aunque el script en línea puede expandirse aún más):Los mismos comandos pero sin importar si hay otros
.md
archivos en los directorios:Ver también:
fuente
Ya sea
o
Para evitar ejecutar uno
sh
por archivo.El
find-sh
es una cadena arbitraria que se convierte en el parámetro posicional cero de la shell,$0
por lo que es algo memorable puede ayudar con la depuración en caso de que la shell encuentre errores (otros pueden sugerir el uso de un parámetro de "omisión" simplesh
o incluso_
predeterminado).fuente
Aquí está el mío. Agregué algunos directorios y archivos más para verificar. También estaba aburrido, así que agregué la última hora modificada y MD5. Tal vez estás buscando duplicados.
fuente
Esto requeriría un poco de lógica.
También puede adaptarlo para que quepa en una línea utilizando bloques de código.
EDITAR: Bash es difícil.
basedir
no es un comando,dirname
no hace lo que pensé que hizo, así que vamos con la expansión de parámetros.fuente
dirname
es el comando que está buscando y las asignaciones no pueden tener espacios alrededor del=
.