मैं एक निर्देशिका ट्री के माध्यम से पुनरावर्ती रूप से कैसे काम करता हूं और प्रत्येक फ़ाइल पर एक विशिष्ट कमांड निष्पादित करता हूं, और बैश में एकल फ़ाइल के लिए पथ, फ़ाइल नाम, एक्सटेंशन, फ़ाइलों और कुछ अन्य विशिष्ट पाठ को आउटपुट करता हूं।
मैं एक निर्देशिका ट्री के माध्यम से पुनरावर्ती रूप से कैसे काम करता हूं और प्रत्येक फ़ाइल पर एक विशिष्ट कमांड निष्पादित करता हूं, और बैश में एकल फ़ाइल के लिए पथ, फ़ाइल नाम, एक्सटेंशन, फ़ाइलों और कुछ अन्य विशिष्ट पाठ को आउटपुट करता हूं।
जवाबों:
जबकि find
समाधान सरल और शक्तिशाली हैं, मैंने एक और अधिक जटिल समाधान बनाने का फैसला किया, जो कि इस दिलचस्प कार्य पर आधारित है , जिसे मैंने कुछ दिनों पहले देखा था।
1. निष्पादन योग्य स्क्रिप्ट फ़ाइल बनाएं, जिसे कहा जाता है walk
, जो /usr/local/bin
शेल कमांड के रूप में सुलभ होने के लिए स्थित है :
sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
nano
: Shift+ में उपयोग करें Insert; Ctrl+ Oऔर Enterबचाने के लिए; Ctrl+ Xबाहर निकलने के लिए।2. स्क्रिप्ट की सामग्री 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. स्पष्टीकरण:
walk()
समारोह का मुख्य तंत्र उसके उत्तर में ज़ाना द्वारा बहुत अच्छी तरह से वर्णित है । इसलिए मैं केवल नए भाग का वर्णन करूंगा।
walk()
फ़ंक्शन के भीतर मैंने यह लूप जोड़ा है:
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
इसका मतलब है $entry
कि प्रत्येक फ़ाइल के लिए फ़ंक्शन निष्पादित किया जाएगा file_specification()
।
फ़ंक्शन file_specification()
के दो भाग हैं। पहले भाग को फ़ाइल से संबंधित डेटा मिलता है - नाम, पथ, आकार आदि। दूसरा भाग डेटा को अच्छी तरह से स्वरूपित करता है। डेटा को प्रारूपित करने के लिए कमांड का उपयोग किया जाता है printf
। और यदि आप स्क्रिप्ट को ट्विक करना चाहते हैं तो आपको इस कमांड के बारे में पढ़ना चाहिए - उदाहरण के लिए यह लेख ।
फ़ंक्शन file_specification()
अच्छी जगह है जहां आप विशिष्ट कमांड रख सकते हैं जिसे प्रत्येक फ़ाइल के लिए निष्पादित किया जाना चाहिए । इस प्रारूप का उपयोग करें:
कमांड "$ {प्रविष्टि}"
या आप कमांड के आउटपुट को वेरिएबल के रूप में सेव कर सकते हैं, और फिर printf
इस वेरिएबल आदि को।:
MY_VAR = "$ ( कमांड " $ {प्रविष्टि} ")" प्रिंटफ "% * s \ tFile आकार: \ t $ {YEL}% s $ {NCL} \ n" $ ((इंडेंट + 4)) '' $ MY_VAR "
या सीधे printf
कमांड का आउटपुट:
प्रिंटफ "% * s \ tFile आकार: \ t $ {YEL}% s $ {NCL} \ n" $ ((इंडेंट + 4)) '' "$ ( कमांड " $ {प्रविष्टि} ")"
भीख मांगने के लिए खंड, कहा जाता है Colourise the output
, कुछ चर है कि printf
उत्पादन को बढ़ावा देने के लिए आदेश के भीतर उपयोग कर रहे हैं । इसके बारे में अधिक जानकारी आपको यहाँ मिल सकती है ।
विभाजन के नीचे अतिरिक्त शर्त जोड़ी जाती है जो पूर्ण और सापेक्ष रास्तों से संबंधित है।
4. उपयोग के उदाहरण:
walk
वर्तमान निर्देशिका के लिए चलाने के लिए:
walk # You shouldn't use any argument,
walk ./ # but you can use also this format
walk
किसी भी बच्चे निर्देशिका के लिए चलाने के लिए:
walk <directory name>
walk ./<directory name>
walk <directory name>/<sub directory>
walk
किसी अन्य निर्देशिका के लिए चलाने के लिए:
walk /full/path/to/<directory name>
walk
आउटपुट के आधार पर एक टेक्स्ट फ़ाइल बनाने के लिए :
walk > output.file
रंग कोड के बिना आउटपुट फ़ाइल बनाने के लिए ( स्रोत ):
walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file
5. उपयोग का प्रदर्शन:
मैं थोड़ा हैरान हूं कि किसी ने अभी तक इसे पोस्ट क्यों नहीं किया है, लेकिन वास्तव bash
में पुनरावर्ती क्षमताएं हैं, यदि आप globstar
विकल्प को सक्षम करते हैं और **
ग्लोब का उपयोग करते हैं । इस तरह, आप (लगभग) शुद्ध bash
स्क्रिप्ट लिख सकते हैं जो इस तरह के पुनरावर्ती ग्लोबस्टार का उपयोग करता है:
#!/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
ध्यान दें कि यहाँ हम फाइलनाम के हिस्सों को प्राप्त करने के लिए पैरामीटर विस्तार का उपयोग करते हैं और हम फ़ाइल के आकार के साथ du
और आउटपुट को छोड़कर बाहरी आदेशों पर निर्भर नहीं हैं awk
।
और जैसा कि यह आपकी निर्देशिका ट्री का पता लगाता है, आपका आउटपुट कुछ इस तरह होना चाहिए:
Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326
स्क्रिप्ट के उपयोग के मानक नियम लागू होते हैं: सुनिश्चित करें कि यह इसके साथ निष्पादन योग्य है chmod +x ./myscript.sh
और इसे वर्तमान निर्देशिका से चलाने ./myscript.sh
या ~/bin
चलाने और चलाने के लिए source ~/.profile
।
"$(file "$i")"
(उपरोक्त स्क्रिप्ट में एक प्रिंटफ के दूसरे भाग के रूप में) वापस आ जाएगी?
output the path, filename, extension, filesize
, इसलिए उत्तर वही है जो पूछा जाता है। :)
आप find
काम करने के लिए उपयोग कर सकते हैं
find /path/ -type f -exec ls -alh {} \;
यह आपकी मदद करेगा यदि आप केवल आकार वाली सभी फ़ाइलों को सूचीबद्ध करना चाहते हैं।
-exec
आपको \;
फ़ाइलों को पार्स करने के लिए उपयोग की जाने वाली प्रत्येक फ़ाइल के लिए कस्टम कमांड या स्क्रिप्ट को निष्पादित करने की अनुमति देगा
, +;
यदि आप उन्हें संक्षिप्त करना चाहते हैं (तो फ़ाइल नाम का अर्थ है)।
find
केवल के साथ ।
find /path/ -type f -printf "path:%h fileName:%f size:%kKB Some Text\n" > to_single_file
या, आप इसके बजाय नीचे उपयोग कर सकते हैं:
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
यदि आप जानते हैं कि पेड़ कितना गहरा है, तो वाइल्डकार्ड का उपयोग करने का सबसे आसान तरीका होगा *
।
शेल स्क्रिप्ट या फ़ंक्शन के रूप में आप जो कुछ भी करना चाहते हैं, उसे लिखें
function thing() { ... }
फिर चला for i in *; do thing "$i"; done
, for i in */*; do thing "$i"; done
... आदि
अपने फ़ंक्शन / स्क्रिप्ट के भीतर, आप उन फ़ाइलों को एकल करने के लिए कुछ सरल परीक्षणों का उपयोग कर सकते हैं, जिनके साथ आप काम करना चाहते हैं और जो कुछ भी आपको उनके साथ करना है।
$i
।
for i in */*
काम करता है। यहाँ, इसे for i in */*; do printf "|%s|\n" "$i"; done
find
यह कर सकता है:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\n'
man find
अन्य फ़ाइल गुणों के लिए एक नज़र है ।
यदि आपको वास्तव में एक्सटेंशन की आवश्यकता है, तो आप इसे जोड़ सकते हैं:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;