Mehrere Verzeichnisse nach best. Dateien/Verz. durchsuchen

M

Morth

Grünschnabel
Hi,
ich bin neu hier und auch shell-programmierung ist für mich recht neu. Aber ich verzweifle langsam und daher hoffe ich ihr könnt mir weiter helfen.

Ubuntu 4.2.4 mit bash

Also:
ich möchte mehrere, bestimmte verzeichnisse nach leerzeichen untersuchen. Je nach fund soll ein bestimmter exit-code gesetzt werden. Das habe ich auch schon geschafft, solange ich nur ein verzeichnis untersuchen will. Aber ich bekomme das absolut nicht hin es für mehrer in einem abwasch zu machen.

hier erstmal meine einzellösung:

Code:
#!/bin/sh

MATCHES_SPACE_1=`find "$1" -maxdepth 1 -regextype posix-extended -regex "^$1/[^ ]*[ ]+[^ ]*$" -printf "%f\n"`

MATCHES_SPACE_3=`find "$1" -maxdepth 1 -regextype posix-extended -regex "^$1/[^ ]*[ ]{3,}[^ ]*$" -printf "%f\n"`

if      [ \( -n "${MATCHES_SPACE_1}" \) -a \( -n "${MATCHES_SPACE_3}" \) ]; then
          echo "CRITICAL: Verzeichnisse/Dateien mit einem oder mehr UND drei oder mehr aufeinander folgenden Leerzeichen gefunden! $1"
          exit 2
elif    [ -n "${MATCHES_SPACE_3}" ]; then
          echo "CRITICAL: Verzeichnisse/Dateien mit drei oder mehr aufeinander folgenden Leerzeichen gefunden! $1"
          exit 2
elif    [ -n "${MATCHES_SPACE_1}" ]; then
          echo "CRITICAL: Verzeichnisse/Dateien mit einem oder mehr aufeinander folgenden Leerzeichen gefunden $1"
          exit 2
else      echo "OK: keine Dateien oder Verzeichnisse mit Leerzeichen gefunden $1"
          exit 0
fi

starten würde ich das ganze mit check.sh /verzeichnis

achja, die verzeichnisse sollen im script stehen und nicht aus einer anderen datei geladen werden, das ist gerade mein grossen problem.

hoffe ihr könnt damit was anfangen ;)

Grüsse, Simon
 
Hi,

wo ist jetzt das Problem? Einfach -maxdepth weglassen, und du hast doch schon was du suchst?

mfg,
bytepool
 
1. Wie willst du mehrmals einen exitcode setzen? Du kannst dein Skript schließlich nur einmal beenden.
2. deine Regexes machen nicht was du denkst/willst.
Code:
$ echo '/foo bar'        |egrep '^/[^ ]*[ ]+[^ ]*$'  # ok soweit.
$ echo '/foo bar baz'  |egrep '^/[^ ]*[ ]+[^ ]*$'  # whoops.
Gut wäre imho auch den String erstmal soweit zu bearbeiten, dass du nicht immer "^$1/" mitmatchen musst, weil dann reicht als Regex einfach "[ ]+".
3.
starten würde ich das ganze mit check.sh /verzeichnis
achja, die verzeichnisse sollen im script stehen und nicht aus einer anderen datei geladen werden
Nach dem ersten quote zu urteilen würd ich sagen, dass das Verzeichnis als Parameter übergeben werden soll, nach dem zweiten quote nicht.

Und zu guter letzt würde ich zu einer Schleife der Art
Code:
find "$1" -type d|while read dir; do
    # mach was du machen willst
done
übergehen. Damit läufst du nur einmal über die Verzeichnisse.
Noch ein Trick zum Leerzeichen zählen:
Code:
$ echo "a aaa    a a      a a                  a a a aaaa  a " |
  perl -ne 'printf "%d\n", length $_ for /[ ]+/g'
Das kann man auch mit vielen pipes und "shell-Mitteln" machen (mir fällt spontan egrep -o, wc -c und expr ein), aber ich würd sagen das machts nicht unbedingt einfacher. Diese Ausgabe kannst jetzt natürlich noch mit sort und uniq bearbeiten.
 
Zuletzt bearbeitet:
1. Wie willst du mehrmals einen exitcode setzen? Du kannst dein Skript schließlich nur einmal beenden.

joa das ist ja auch mein problem, ich muss die überprüfung der verzeichnisse irgendwie vorher machen

2. deine Regexes machen nicht was du denkst/willst.
Code:
$ echo '/foo bar'        |egrep '^/[^ ]*[ ]+[^ ]*$'  # ok soweit.
$ echo '/foo bar baz'  |egrep '^/[^ ]*[ ]+[^ ]*$'  # whoops.
Gut wäre imho auch den String erstmal soweit zu bearbeiten, dass du nicht immer "^$1/" mitmatchen musst, weil dann reicht als Regex einfach "[ ]+".

hmm aalso ich dachte ich hätte alles durchgespielt, aber wenn ich die grenzen weglasse müsste das allerdings reichen... teste ich morgen mal... ich hab halt noch nicht so den überblick mit welchen befehlen was machen kann und welche parameter man da am besten nimmt.
also ich dachte so an find, awk und egrep, aber für tips bin ich dankbar

3.

Nach dem ersten quote zu urteilen würd ich sagen, dass das Verzeichnis als Parameter übergeben werden soll, nach dem zweiten quote nicht.

also ich würde ja auch gerne dann die verzeichnisse in eine variable oder in mehrere schreiben, wenn das irgendwie geht sowas wie
Code:
Liste=( vz1 vz2 vz3)
aber keine ahnung ob sowas geht oder halt
Code:
vz1="/verzeichnis1"
vz2="/verzeich/nis2"

Und zu guter letzt würde ich zu einer Schleife der Art
Code:
find "$1" -type d|while read dir; do
    # mach was du machen willst
done
übergehen. Damit läufst du nur einmal über die Verzeichnisse.
ja eine schleife wäre mein nächster anlauf gewesen, aber irgendwie fehlt mir der ansatz, wie ich das mit mehreren verzeichnissen mache

Noch ein Trick zum Leerzeichen zählen:
Code:
$ echo "a aaa    a a      a a                  a a a aaaa  a " |
  perl -ne 'printf "%d\n", length $_ for /[ ]+/g'
Das kann man auch mit vielen pipes und "shell-Mitteln" machen (mir fällt spontan egrep -o, wc -c und expr ein), aber ich würd sagen das machts nicht unbedingt einfacher. Diese Ausgabe kannst jetzt natürlich noch mit sort und uniq bearbeiten.

puuh ja ich weiss, perl ist da viel mächtiger, aber das kann ich noch wenier :(
naja ich glaub das hilft aber nix, ohne daraus ein monster zu machen
.
.
.
EDIT (autom. Beitragszusammenführung) :
.

Hi,

wo ist jetzt das Problem? Einfach -maxdepth weglassen, und du hast doch schon was du suchst?

mfg,
bytepool

joa, aber alles in unterverzeichnissen interessiert mich nicht. die verzeichnisse sollen beliebig wählbar sein und nur in deren ebene gesucht werden
 
Zuletzt bearbeitet:
joa das ist ja auch mein problem, ich muss die überprüfung der verzeichnisse irgendwie vorher machen
Hm, ich hab meine Verwirrung wohl nicht klar genug gemacht. Ich versteh einfach nicht was du genau machen willst.

Soll das Skript mit exit-code 2 enden falls in einem der (auf der Kommandozeile übergebenen) Verzeichnisse ein Verzeichnis mit Leerzeichen enthalten ist? (In der "ersten Ebene", also find -maxdepth 1)
Falls ja, dann kannst du doch einfach alle Verzeichnisse entsprechend testen und einfach beim ersten Leerzeichen "exit 2" machen. (Oder hab ich was falsch verstanden?)
Falls nein versteh ich nicht wie das funktionieren soll, aber du könntest es vielleicht mit einer Variable machen und am Ende des Skripts einfach 'exit $exit_var' oder so.

also ich würde ja auch gerne dann die verzeichnisse in eine variable oder in mehrere schreiben, wenn das irgendwie geht sowas wie [...]
Ja natürlich geht das, meine Frage war eher, wo kommen die Verzeichnisse her, von der Kommandozeile?
Falls ja, so sind sie bereits in einem Array:
Code:
$ cat foo.sh
#!/bin/sh
for arg in "$@"; do
    echo "$arg"
done
$ ./foo.sh '/foo' './bar baz/asdf' '/  setsamer/verzeichnis/name/'
/foo
./bar baz/asdf
/  setsamer/verzeichnis/name/
Zur while-Schleife: wenn ich dich diesmal richtig(er) verstehe, dann sollte das eine geschachtelte Schleife sein, "außen" über die Verzeichnisnamen und innen über die "Inhalte" (also das, was find "$verzeichnisname" -maxdepth 1 liefert) iterieren, wobei die äußere Schleife dann wie oben eine for- und die innere Schleife wie im letzten Post eine while-Schleife wär.
Aber das sind nur Empfehlungen, machs so wie dus verstehst.

Btw, du packst Verzeichnisnamen in deinem find-Einzeiler einfach in eine Regex ohne Sonderzeichen entsprechend zu escapen. (Was wenn $1="[]"? Syntaxfehler, whoops..) Und damit will ich *nicht* suggerieren, dass du jetzt die Dinger erst escapen und dann in die Regex stecken sollst. Eher würd ich, wie im vorigen Post schon vorgeschlagen, sehen, dass du den Anfang ("^$1/") nicht jedesmal mitmatchen musst; denn das ist fehleranfällig und redundant.
 
Hi,
joa, aber alles in unterverzeichnissen interessiert mich nicht. die verzeichnisse sollen beliebig wählbar sein und nur in deren ebene gesucht werden
ah, das ist der Knackpunkt, das hatte ich nicht verstanden. Ich denke dann habe ich jetzt ungefaehr verstanden was du willst, aber du solltest in Zukunft versuchen im ersten Post noch deutlicher zu sein. Mir ist klar dass eine gute Beschreibung nicht immer einfach ist, erst Recht wenn man selbst schon voll drin steckt. Aber wenn das Problem nicht verstanden wird, kann man dir nicht helfen, wie du ja vielleicht auch an unseren Fragen merkst.

In dem Fall halte ich find fuer ziemlich ueberfluessig. Dann wuerde ich die Verzeichnisse lieber in einen Array schreiben und ueber alle Dateien in dem Verzeichnis via globbing iterieren. Dann kannst du naemlich in aller Ruhe pro Dateiname alle moeglichen tests durchfuehren die dir in den Sinn kommen, ohne dich selbst auf eine regex zu beschraenken.

z.B. so in der Art, um ar0's Beispiel mal aufzugreifen:
Code:
for dir in "$@"; do
    for file in "$dir/*"; do
	echo "$file" # processing here                                                           
    done
done
Das ist einfach flexibler. Aber es gilt wie immer, viele Wege fuehren nach Rom, es geht sicher auch mit find und regexen (eine neue Echsenart, legen auch Eier, fressen sie aber nicht).

mfg,
bytepool
 
sry wenn ich das vielleicht nicht so richtig rüber gebracht habe, aber ich versuche mein bestes. ist alles totales neuland für mich und ich versuche mein bestes nicht alles durcheinander zu schmeissen.

ich danke euch schon mal sehr für eure hartnäckigen Bemühungen mir zu helfen. Hier kamen jetzt gleich mal 4 oder 5 Begriffe die wohl wichtig sind und ich muss jetzt erstmal wieder ne ganze Weile Manuals studieren. Ich hab einfach noch nicht den überblick, welche werkzeuge ich alles habe.

aber das mit array hört sich schon mal sehr gut an, das kenne ich aus C.

die Verzeichnisse sollen im script dann "hardcoded" werden.

so ich werd mal machen und melde mich wenn ichs hinbekommen habe oder euch weiter nerven muss ;)
 
Hi,

z.B. so in der Art, um ar0's Beispiel mal aufzugreifen:
Code:
for dir in "$@"; do
    for file in "$dir/*"; do
	echo "$file" # processing here                                                           
    done
done

So ich habe das mal getestet. Es funktioniert auch wunderbar, nur habe ich jetzt ein neues Problem:
bei

for VARIABLE in WORT do

werden die Elemente in WORT durch Leerzeichen getrennt. Nun habe ich aber gerade Elemente, die x Leerzeichen enthalten können. Somit werden die ursprünglichen Elemente zerstückelt.

Ich hatte das z.B. so gemacht:

Code:
#!/bin/bash
VERZEICHNISSE="/var /var/www"

for dir in "$VERZEICHNISSE"; do
    for file in `ls -A $dir`; do
        echo "$file" # processing here
     done
done
Wenn ich z.B. eine Datei "test test" habe kommt als Ausgabe

test
test

raus.

Irgendwie müsste das Zeilenweise gehen, dann wärs kein Problem denke ich.
 
Wenn dus denn unbedingt hardcoden willst, pack die Verzeichnisse halt in ein Array.
Code:
$ cat foo.sh
#!/bin/sh
dirs=( "/foo" "./bar baz/asdf" "/  setsamer/verzeichnis/name/" )

for dir in "${dirs[@]}"; do
    echo "$dir"
done
$ ./foo.sh
/foo
./bar baz/asdf
/  setsamer/verzeichnis/name/

relevanter Teil von man bash:
Code:
If
       the word is double-quoted, ${name[*]} expands to a single word with the
       value  of each array member separated by the first character of the IFS
       special variable, and ${name[@]} expands each element of name to a sep‐
       arate  word.   When  there  are no array members, ${name[@]} expands to
       nothing.
Das der obige code so funktioniert ist also kein Zufall, sondern per Design.
bash expandiert "${a[@]}" zu "${a[0]}" "${a[1]}" etc., also perfekt für for-Schleifen. (Und btw, "$@" expandiert analog zu "$1" "$2" etc.)

edit:
und für die innere Schleife würde ich wieder eine while-read Kombo empfehlen. Entweder mit "ls -a1" oder "find -maxdepth 1".
 
Zuletzt bearbeitet:
So ich habe das mal getestet.
Nicht ganz, du hast das globbing (/*) druch eine sub-shell (`ls -A $dir`) ersetzt, was zu deinem Problem fuehrt. Aber ich kann verstehen dass der Unterschied fuer einen Skripting Neuling nicht direkt ersichtlich ist.

Mache aus `ls -A $dir` wieder "$dir/*", und es sollte funktionieren wie du es dir vorstellst. Den Grund kann ich dir allerdings grade nicht erklaeren, da ich A zu faul bin, und B ohne nachzugucken selbst nicht zu 100% sicher bin. ;)

Ist schon wieder ne Weile her dass ich mir die Details angeguckt hab, vielleicht weiss es ja jemand anders so aussem Kopf.
<edit>
Naja, es hat jedenfalls damit zu tun, dass die Zeilenumbrueche vom Output der sub-shell durch Leerzeichen ersetzt werden (weiss nich mehr ob ls oder die Bash das macht).
Eventuell verstehen neuere Bash Versionen sogar "`ls -A $dir`". "*" wird jedenfalls immer korrekt expandiert.
</edit>

Und bei Verzeichnissen mit Leerzeichen beachten was ar0 schon schrieb.

mfg,
bytepool
 
Zuletzt bearbeitet:
@bytepool: du darfst den Asterisk nicht quoten, sonst wird er nicht expandiert. Außerdem bekommt man so keine versteckten Dateien, falls die Option dotglob nicht gesetzt ist: (die afaik per default deaktiviert ist)
Code:
set -o
[...]
              dotglob If set, bash includes filenames beginning with a ‘.’ in the results of pathname expansion.


Und zu newlines in command substitution:
Code:
$ set|grep IFS
IFS=$' \t\n'
Das ist glaub ich auch der Default-Wert.
 
@bytepool: du darfst den Asterisk nicht quoten, sonst wird er nicht expandiert.
Da muesstest du dich auf eine genaue Shell und Version festlegen, denn das hat bei mir noch in jeder Bash Version funktioniert:
Code:
$ for i in "*"; do echo $i; done
bar.txt bar.txt~ foo.sh foo.sh~ getopts
Funktioniert hier prima mit
Code:
$ bash --version
GNU bash, version 4.1.0(1)-release (i486-pc-linux-gnu)
$ # und
$ bash --version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
$ # und
$ dpkg -s dash | grep Version
Version: 0.5.5.1-3

mfg,
bytepool
 
Funktioniert hier prima mit
Das hängt wahrscheinlich davon ab was du unter "funktioniert prima" verstehst.
Code:
for i in "*"; do echo "$i"; done

Wenn du schaun willst wie etwas expandiert wird ist ein herzliches
Code:
$ echo "*"
angebrachter.
 
Tatsaechlich, ich glaube dann verstehe ich zum ersten Mal, wieso 'for i in "*"' tut was es tut. ;)
D.h. die for-Schleife bekommt tatsaechlich das Sternchen als Argument uebergeben, und die Schleife sorgt dann fuer die korrekte Expansion.

Trotzdem ist es gerade deswegen allein mit den Quotes korrekt, ansonsten hast du wieder das Leerzeichen Problem.

Aber danke fuer die Aufklaerung.

mfg,
bytepool
 
sauber, das mit dem expandieren der arrays ist ja cool :)

aber "while read" hab ich noch nicht voll geschnallt, ich musste mir was basteln, damit es bei mir funktioniert hat.
aber ich komme der sache schon sehr viel näher, dank euch!

Code:
#!/bin/bash
VERZEICHNISSE=("/var" "/var/tmp")

for dir in "${VERZEICHNISSE[@]}"; do
        lines() {
                while read line; do
                echo "$line++++"
                done
                }
        ls -1AQ $dir | lines
done

ohne die funktion und die pipe habe ich es nicht hinbekommen die lines per kommando an while read zu übergeben.

zu ls -1AQ:
-1 weil zeilenweise (klar, war ja der Sinn am while read)
-A damit ich nicht . und .. mitbekomme, aber trotzdem .datei
-Q alle Zeilen in " ", weil ich festgestellt habe, dass sonst Leerzeichen am Ende einer übergebenen Zeile abgeschnitten werden.
 
Code:
$ echo -e "foo   \n   bar \n    baz    "|while read; do echo ">>>$REPLY<<<"; done
>>>foo   <<<
>>>   bar <<<
>>>    baz    <<<


"read variable" does word splitting, hence the removal of blanks.
"read" assigns /the whole line/ to $REPLY.
von hier.
 
D.h. die for-Schleife bekommt tatsaechlich das Sternchen als Argument uebergeben, und die Schleife sorgt dann fuer die korrekte Expansion.
Argh, WTF?
Code:
$ for i in "*"; do echo "$i"; done
*
Ich geh mich jetzt mal in der naechsten Ecke schaemen, und geh lieber schlafen. ;p

Edit:
Mhh, ja, so war das:
Code:
for i in *; do echo "$i"; done
Da hab ich aber ein paar Dinge gewaltigst durcheinander gebracht... Hoechste Zeit dass ich mal wieder mein Bash Buch rauskrame, das sind eigentlich Sachen die ich wissen muss...
 
Zuletzt bearbeitet:
Ich geh mich jetzt mal in der naechsten Ecke schaemen, und geh lieber schlafen.
Pff, ich war auch *sehr* erstaunt als ich deinen Befehl ausprobiert habe und dachte woah, bestimmt so'n "magical" bash feature. Ist aber auch wirklich selten, dass man zwei subtile Fehler macht die sich gegenseitig exakt auscanceln, hehe. Deswegen, Variablen einfach immer double-quoten und man hat weniger Kopfschmerzen.
 
die variante mit while read ohne variable sondern mit REPLY ist natürlich besser für meinen Fall, dann spare ich mir das quoting

genau solche tricks habe ich gehofft hier zu erfahren, danke! :)
.
.
.
EDIT (autom. Beitragszusammenführung) :
.

Code:
$ echo -e "foo   \n   bar \n    baz    "|while read; do echo ">>>$REPLY<<<"; done
>>>foo   <<<
>>>   bar <<<
>>>    baz    <<<



von hier.

kann man aber auch umgehen mit
Code:
local IFS=$'\n'   #damit wird der Feldtrenner vom Leerzeichen zum Zeilenumbruch"
while read i; do echo "$i" done #damit funktioniert es hier auch wieder wenn Elemente Leerzeichen enthalten sollen
 
Zuletzt bearbeitet:

Ähnliche Themen

Log-Datei bearbeiten

Keine grafische Oberfläche (Debian Installation)

Alle Dateien eines Verzeichnisses mit einer anderen Datei vergleichen

[Gelöst] Suchen und ersetzen mit Hilfe mehrerer Parameter

verzeichniss suche funktioniert nicht

Zurück
Oben