Quero encontrar todas as subpastas, que contenham um arquivo de remarcação com o mesmo nome (e extensão .md
).
Por exemplo: Desejo 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](/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
efoo/bar.md
devefoo
ser incluído ou excluído?-printf
com find, você pode obter qualquer parte do jogo você quer, ver a minha ediçãoRespostas:
Supondo que seus arquivos tenham nomes sensatos, ou seja, não há necessidade
-print0
etc. Você pode fazer isso com o GNU, assim:Resultado:
Se você deseja apenas o nome do diretório, adicione um
-printf
argumento:Saída quando executado nos dados de teste atualizados:
fonte
find . -type f | egrep '.*/([^/]+)/\1\.md$'
print0
.%h
em printf é usado para o tipo int de dados a serem formatados. Referência: string de formato printf - Wikipedia . Poderia explicar essa parte? Como está%h
sendo usado aqui?find
, consulte a seção 3.2.2.1 no manual para obter mais detalhes.Em um sistema GNU, você pode fazer algo como:
fonte
zsh
solução anterior (acredito que tenha duas das votações anteriores) e sinta-se à vontade para apontar quaisquer falhas nela que possam ter solicitado que você a remova.O exemplo 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 de remarcação com o mesmo nome que o 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 na sinalização).O
set -- "$dirpath"/*.md
bit definirá os parâmetros posicionais para a lista de nomes de caminhos que correspondem ao padrão (corresponde a qualquer nome com um sufixo.md
no diretório). Podemos usar$#
mais tarde para ver quantas correspondências obtivemos disso.Se o script shell sair com sucesso,
-print
imprimirá o caminho para o diretório encontrado.Versão ligeiramente 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
find
(o script embutido pode ser expandido ainda mais):Os mesmos comandos, mas sem se preocupar se há outros
.md
arquivos nos diretórios:Veja também:
fonte
Ou
ou
Para evitar a execução de um
sh
por arquivo.O
find-sh
é uma string arbitrária que se torna o parâmetro zeroth de posição do shell$0
- tornando-o algo memorável pode ajudar na depuração caso o shell encontre erros (outros podem sugerir o uso de parâmetros simplessh
ou mesmo_
como um parâmetro "skip" padrão).fonte
Aqui está o meu. Eu adicionei mais alguns diretórios e arquivos para verificar. Eu também estava entediado, então adicionei o horário da última modificação e o MD5. Talvez você esteja procurando duplicatas.
fonte
Isso exigiria um pouco de lógica.
Você também pode adaptar isso para caber em um liner usando blocos de código.
EDIT: Bash é difícil.
basedir
não é um comando,dirname
não faz o que eu pensava, então vamos com a expansão de parâmetros.fonte
dirname
é o comando que você está procurando e as atribuições não podem ter espaços em torno do=
.