Como trabalho recursivamente através de uma árvore de diretórios e executo um comando específico em cada arquivo, e envia o caminho, nome do arquivo, extensão, tamanho do arquivo e algum outro texto específico para um único arquivo no bash.
Como trabalho recursivamente através de uma árvore de diretórios e executo um comando específico em cada arquivo, e envia o caminho, nome do arquivo, extensão, tamanho do arquivo e algum outro texto específico para um único arquivo no bash.
Respostas:
Embora as find
soluções sejam simples e poderosas, decidi criar uma solução mais complicada, baseada nessa interessante função que vi alguns dias atrás.
1. Crie um arquivo de script executável, chamado walk
, localizado /usr/local/bin
para ser acessível como comando shell:
sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
nano
: Shift+ Insertpara colar; Ctrl+ Oe Enterpara salvar; Ctrl+ Xpara sair.2. O conteúdo do script walk
é:
#!/bin/bash
# Colourise the output
RED='\033[0;31m' # Red
GRE='\033[0;32m' # Green
YEL='\033[1;33m' # Yellow
NCL='\033[0m' # No Color
file_specification() {
FILE_NAME="$(basename "${entry}")"
DIR="$(dirname "${entry}")"
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
SIZE="$(du -sh "${entry}" | cut -f1)"
printf "%*s${GRE}%s${NCL}\n" $((indent+4)) '' "${entry}"
printf "%*s\tFile name:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$FILE_NAME"
printf "%*s\tDirectory:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$DIR"
printf "%*s\tName only:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$NAME"
printf "%*s\tExtension:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$EXT"
printf "%*s\tFile size:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$SIZE"
}
walk() {
local indent="${2:-0}"
printf "\n%*s${RED}%s${NCL}\n\n" "$indent" '' "$1"
# If the entry is a file do some operations
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
# If the entry is a directory call walk() == create recursion
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+4)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
echo
3. Explicação:
O principal mecanismo da walk()
função é muito bem descrito por Zanna em sua resposta . Então, vou descrever apenas a nova parte.
Dentro da walk()
função eu adicionei este loop:
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
Isso significa que para cada $entry
arquivo que será executado, a função será executada file_specification()
.
A função file_specification()
tem duas partes. A primeira parte obtém dados relacionados ao arquivo - nome, caminho, tamanho etc. A segunda parte gera os dados em uma forma bem formatada. Para formatar os dados é usado o comando printf
. E se você deseja ajustar o script, leia sobre este comando - por exemplo, este artigo .
A função file_specification()
é um bom local para colocar o comando específico que deve ser executado para cada arquivo . Use este formato:
comando "$ {entry}"
Ou você pode salvar a saída do comando como variável e, em seguida, printf
esta variável, etc .:
MY_VAR = "$ ( comando " $ {entry} ")" printf "% * s \ tTamanho do arquivo: \ t $ {YEL}% s $ {NCL} \ n" $ ((recuo + 4)) '' "$ MY_VAR"
Ou diretamente printf
a saída do comando:
printf "% * s \ tTamanho do arquivo: \ t $ {YEL}% s $ {NCL} \ n" $ ((recuo + 4)) '' "$ ( comando " $ {entry} ")"
A seção ao pedido, chamada Colourise the output
, inicializa algumas variáveis que são usadas no printf
comando para colorir a saída. Mais sobre isso você pode encontrar aqui .
Na parte inferior do script, é adicionada uma condição adicional que lida com caminhos absolutos e relativos.
4. Exemplos de uso:
Para executar walk
no diretório atual:
walk # You shouldn't use any argument,
walk ./ # but you can use also this format
Para executar walk
em qualquer diretório filho:
walk <directory name>
walk ./<directory name>
walk <directory name>/<sub directory>
Para executar walk
em qualquer outro diretório:
walk /full/path/to/<directory name>
Para criar um arquivo de texto, com base na walk
saída:
walk > output.file
Para criar um arquivo de saída sem códigos de cores ( origem ):
walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file
5. Demonstração de uso:
Estou um pouco perplexo com o motivo de ninguém ter postado ainda, mas, de fato bash
, possui recursos recursivos, se você ativar a globstar
opção e usar o **
glob. Como tal, você pode escrever (quase) um bash
script puro que usa a globstar recursiva como esta:
#!/usr/bin/env bash
shopt -s globstar
for i in ./**/*
do
if [ -f "$i" ];
then
printf "Path: %s\n" "${i%/*}" # shortest suffix removal
printf "Filename: %s\n" "${i##*/}" # longest prefix removal
printf "Extension: %s\n" "${i##*.}"
printf "Filesize: %s\n" "$(du -b "$i" | awk '{print $1}')"
# some other command can go here
printf "\n\n"
fi
done
Observe que aqui usamos a expansão de parâmetros para obter as partes do nome do arquivo que queremos e não confiamos em comandos externos, exceto para obter o tamanho do arquivo du
e limpar a saída com awk
.
E como ele atravessa sua árvore de diretórios, sua saída deve algo como isto:
Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326
Aplicam-se regras padrão de uso de script: verifique se ele é executável chmod +x ./myscript.sh
e execute-o no diretório atual via ./myscript.sh
ou coloque-o ~/bin
e execute-o source ~/.profile
.
"$(file "$i")"
(no script acima como segunda parte de um printf) retornariam?
output the path, filename, extension, filesize
, portanto a resposta corresponde ao que é solicitado. :)
Você pode usar find
para fazer o trabalho
find /path/ -type f -exec ls -alh {} \;
Isso o ajudará se você quiser apenas listar todos os arquivos com tamanho.
-exec
permitirá que você execute comandos ou scripts personalizados para cada arquivo
\;
usado para analisar arquivos um por um, você pode usar +;
se quiser concatená-los (significa nomes de arquivos).
Com find
apenas.
find /path/ -type f -printf "path:%h fileName:%f size:%kKB Some Text\n" > to_single_file
Ou então, você pode usar abaixo:
find -type f -not -name "to_single_file" -execdir sh -c '
printf "%s %s %s %s Some Text\n" "$PWD" "${1#./}" "${1##*.}" $(stat -c %s "$1")
' _ {} \; > to_single_file
find -printf
). 1
Se você souber a profundidade da árvore, a maneira mais fácil será usar o curinga *
.
Escreva tudo o que você deseja fazer como um shell script ou uma função
function thing() { ... }
em seguida, executar for i in *; do thing "$i"; done
, for i in */*; do thing "$i"; done
... etc
Dentro da sua função / script, você pode usar alguns testes simples para destacar os arquivos com os quais deseja trabalhar e fazer o que for necessário com eles.
$i
.
for i in */*
funciona. Aqui, teste-o:for i in */*; do printf "|%s|\n" "$i"; done
find
posso fazer isso:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\n'
Dê uma olhada em man find
outras propriedades do arquivo.
Se você realmente precisa da extensão, pode adicionar este:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;