Eu quero encontrar todas as subpastas, que contém um arquivo markdown com o mesmo nome (e extensão .md
).
Por exemplo: Eu quero encontrar as seguintes subpastas:
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: Pode haver outros arquivos ou subdiretórios no diretório.
Alguma sugestão?
As soluções para o problema podem ser testadas usando o seguinte 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](https://unix.stackexchange.com/questions/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 "$@"
Assumindo que seus arquivos são nomeados de forma sensata, ou seja, não há necessidade de
-print0
etc. Você pode fazer isso com o GNU find assim:Resultado:
Se você quiser apenas o nome do diretório, adicione um
-printf
argumento:Saída quando executado em seus dados de teste atualizados:
O acima encontraria todos os diretórios abaixo do diretório atual (incluindo o diretório atual) e executaria um script de shell curto para cada um.
O código do shell testaria se há um arquivo markdown com o mesmo nome do diretório dentro do diretório e se esse é o único
*.md
nome nesse diretório. Se esse arquivo existir e se for o único*.md
nome, o script de shell embutido sairá com um status de saída zero. Caso contrário, ele sai com um status de saída diferente de zero (falha de sinalização).O
set -- "$dirpath"/*.md
bit definirá os parâmetros posicionais para a lista de nomes de caminho correspondentes ao padrão (corresponde a qualquer nome com um sufixo.md
no diretório). Podemos usar$#
mais tarde para ver quantas correspondências conseguimos com isso.Se o shell script sair com sucesso,
-print
imprimirá o caminho para o diretório encontrado.Versão um pouco mais rápida que usa menos invocações do script embutido, mas isso não permite que você faça mais com os nomes de caminho encontrados em
find
si (o script embutido pode ser expandido ainda mais):Os mesmos comandos, mas sem se importar se existem outros
.md
arquivos nos diretórios:Veja também:
Em um sistema GNU, você poderia fazer algo como:
Qualquer
ou
Para evitar a execução de um
sh
por arquivo.O
find-sh
é uma string arbitrária que se torna o parâmetro posicional zero do shell$0
- torná-lo algo memorável pode ajudar na depuração caso o shell encontre erros (outros podem sugerir o uso simplessh
ou mesmo_
como um parâmetro "skip" padrão).Aqui está o meu. Eu adicionei mais alguns diretórios e arquivos para verificar. Eu também estava entediado, então adicionei a última hora modificada e o MD5. Talvez você esteja procurando por duplicatas.
Isso exigiria um pouco de lógica.
Você também pode adaptar isso para caber em um forro usando blocos de código.
EDIT: Bash é difícil.
basedir
não é um comando,dirname
não faz o que eu pensei que fizesse, então vamos com a expansão de parâmetros.