Bir dizin ağacı üzerinden özyinelemeli olarak nasıl çalışabilir ve her dosyada belirli bir komutu yürütebilirim ve yol, dosya adı, uzantı, dosya boyutu ve diğer bazı belirli metinleri bash içindeki tek bir dosyaya çıktılar.
Bir dizin ağacı üzerinden özyinelemeli olarak nasıl çalışabilir ve her dosyada belirli bir komutu yürütebilirim ve yol, dosya adı, uzantı, dosya boyutu ve diğer bazı belirli metinleri bash içindeki tek bir dosyaya çıktılar.
Yanıtlar:
findÇözümler basit ve güçlü olsa da , birkaç gün önce gördüğüm bu ilginç işleve dayanan daha karmaşık bir çözüm oluşturmaya karar verdim .
1. denilen yürütülebilir komut dosyası oluşturun walkbulunan, /usr/local/binkabuk komutu olarak erişilebilir olması için:
sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
nano: Shift+ Insertfor paste; Ctrl+ Ove Enterkaydetmek için; Ctrl+ Xçıkış için.2. Komut dosyasının içeriği 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. Açıklama:
walk()Fonksiyonun ana mekanizması cevabında Zanna tarafından oldukça iyi tanımlanmıştır . Bu yüzden sadece yeni kısmı anlatacağım.
walk()İşlev içinde bu döngüyü ekledim:
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
Bu $entry, bir dosya olan her biri için işlev yürütüleceği anlamına gelir file_specification().
Fonksiyonun file_specification()iki bölümü vardır. İlk bölüm, dosya adı, yol, boyut vb. İle ilgili verileri alır. İkinci bölüm verileri iyi biçimlendirilmiş biçimde çıkarır. Verileri biçimlendirmek için komut kullanılır printf. Senaryoyu değiştirmek istiyorsanız, bu komut hakkında okumalısınız - örneğin bu makale .
İşlev file_specification(), her dosya için yürütülmesi gereken belirli komutu koyabileceğiniz iyi bir yerdir . Bu biçimi kullanın:
"$ {entry}" komutu
Veya komutun çıktısını değişken olarak kaydedebilir ve daha sonra printfbu değişken, vb .:
MY_VAR = "$ ( " $ {entry} ") komutu "
printf "% * s \ tDosya boyutu: \ t $ {YEL}% s $ {NCL} \ n" $ ((girinti + 4)) '' "$ MY_VAR"
Veya doğrudan printfkomutun çıktısı:
printf "% * s \ tDosya boyutu: \ t $ {YEL}% s $ {NCL} \ n" $ ((girinti + 4)) '' "$ ( komut " $ {entry} ")" Dilenilen bölüm olarak adlandırılan bölüm , çıktıyı renklendirmek için komut Colourise the outputiçinde kullanılan birkaç değişkeni başlatır printf. Bununla ilgili daha fazla bilgiyi burada bulabilirsiniz .
Komut dosyasının altına mutlak ve göreli yollar ile ilgili ek koşul eklenir.
4. Kullanım örnekleri:
walkGeçerli dizinde çalıştırmak için:
walk # You shouldn't use any argument,
walk ./ # but you can use also this formatwalkHerhangi bir alt dizinde çalıştırmak için:
walk <directory name>
walk ./<directory name>
walk <directory name>/<sub directory>walkBaşka bir dizinde çalıştırmak için:
walk /full/path/to/<directory name>Çıktıya dayalı olarak bir metin dosyası oluşturmak için walk:
walk > output.fileRenk kodları olmadan çıktı dosyası oluşturmak için ( kaynak ):
walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file5. Kullanım gösterimi:
Neden henüz kimsenin yayınlamadığı konusunda biraz şaşkınım, ancak seçeneği bashetkinleştirir globstarve **glob kullanırsanız gerçekten özyinelemeli yeteneklere sahiptir . Bu şekilde, bash bu özyinelemeli globstar'ı kullanan (neredeyse) saf komut dosyası yazabilirsiniz:
#!/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
Burada istediğimiz dosya adının parçalarını almak için parametre genişletme kullandığımıza ve dosya boyutunu almak duve çıktıyı temizlemek dışında harici komutlara güvenmediğimize dikkat edin awk.
Dizin ağacınızı geçtikçe, çıktınız şöyle olmalıdır:
Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326
Komut dosyası kullanımının standart kuralları geçerlidir: çalıştırılabilir olduğundan emin olun chmod +x ./myscript.shve geçerli dizinden çalıştırın ./myscript.shveya yerleştirin ~/binve çalıştırın source ~/.profile.
"$(file "$i")"(yukarıdaki komut dosyasında bir printf'in ikinci kısmı olarak) MIME bilgisini gerçekten istiyorsunuz ?
output the path, filename, extension, filesize , bu yüzden cevap sorulanla eşleşiyor. :)
findİşi yapmak için kullanabilirsiniz
find /path/ -type f -exec ls -alh {} \;
Yalnızca boyutu olan tüm dosyaları listelemek istiyorsanız bu size yardımcı olacaktır.
-exec\;dosyaları tek tek ayrıştırmak için kullanılan her dosya için özel komut veya komut dosyası yürütmenize izin verir
, +;bunları birleştirmek istiyorsanız kullanabilirsiniz (dosya adları anlamına gelir).
İle findsadece.
find /path/ -type f -printf "path:%h fileName:%f size:%kKB Some Text\n" > to_single_file
Veya bunun yerine aşağıda kullanabilirsiniz:
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
Ağacın ne kadar derin olduğunu biliyorsanız, en kolay yol joker karakteri kullanmak olacaktır *.
Kabuk betiği veya işlev olarak yapmak istediğiniz her şeyi yazın
function thing() { ... }
sonra koş for i in *; do thing "$i"; done,, for i in */*; do thing "$i"; done... vs
İşleviniz / komut dosyanızda, çalışmak istediğiniz dosyaları belirlemek ve onlarla ne yapmak istiyorsanız onu yapmak için bazı basit testleri kullanabilirsiniz .
$i.
for i in */*çalıştığını anlamıyorsun . İşte, test edin:for i in */*; do printf "|%s|\n" "$i"; done