Bash: if und grep

Das stimmt zwar, aber schau mal genau hin, was du als Referenz mit -d reingibst: nur die Tagesangabe. Und das führt zu Mitternacht. Probier's einfach mal aus ohne das +%s.

Macht ja auch nichts weil ich beide Male nur mit dem Datum ohne Uhrzeit verglichen hatte. Die Sekunden haben dann halt nur Tagessprünge aber deswegen stimmt das Endergebnis (Tage) trotzdem.
 
Wie man hier im Thread gut sieht, ist Bash zwar eine nette Shell, jedoch zum Programmieren eher nicht so ganz geeignet.
Die ganzen unklaren Reaktionen sind nicht mein Fall. In der nächsten Bash version reagiert das Skript vielleicht ganz anders.
Das wär mir zu unsicher.
In Perl oder meinetwegen in Python wäre das Skript keine große Sache gewesen.
 
der IFS (Array-Wert-Trenner) ist immer "LEERSTELLE" . Diesen könnte man auch ändern allerdings muss man diesen dann wieder zurücksetzen etc was nervt.

zu deiner Zeile fügt da einfach am ende jedes wertes ein leerzeichen ein:

Code:
for snapshot in "$zfssnaplist"; do
$snapshotndate=$(echo "$snapshot" | awk '{ print $4 "-" $3 "-" $6" " }' | tr -d '\n')
echo "snap: $snapshotdate\n"

das echo was du da machst ist auch falsch ein Array immer mit $(snapshotdate
[*]) angeben. $snapshotdate gibt imemr den letzten wert aus.
der * steht für alle kann aber 0-9999 die jeweiligen array wert ausgeben. $(snapshotdate[0]) = 1 wert des array

Mein Vorschlag für nen Löschscript mit Zeit löscht alles älter als $DELETE_DAYS

Code:
#!/bin/sh
LC_ALL=C


DELETE_DAYS=2
SECONDS_PER_DAY=86400
TIMESTAMP=`date +%s`
DELETE_OLD_TIME=$(( $TIMESTAMP - $DELETE_DAYS * $SECONDS_PER_DAY ))
echo "Löschzeit $DELETE_OLD_TIME"


zfs list -H -t snapshot -o name,creation -s creation  | while read snapshotname wochentag monat tag uhrzeit jahr ;do
echo -en "Name: $snapshotname ### Tag: $tag.$monat.$jahr ### Wochentag: $wochentag ### Datum-Timestamp: `date --d "$tag-$monat-$jahr $uhrzeit" +%s`"
if [ `date --d "$tag-$monat-$jahr $uhrzeit" +%s` -lt $DELETE_OLD_TIME ];then
        echo " ### Delete $snapshotname"
else
        echo ""
fi
done
 
Zuletzt bearbeitet:
Ich bin mittlerweile schon etwas weiter als das

Code:
# clean old zfs snapshots according to keep interval
echo "### Start cleanup of ZFS snapshots ###"

# put . in variable if not set, otherwise grep fails
if [ -z "$zfslabel" ]; then
    zfslabel="."
fi
if [ -z "$zfsfolder" ]; then
    zfsfolder="."
fi

# use LC_ALL=C for getting output in english (otherwise months are not english and can not be handled by date command)
# -s creation sorts oldest first
if [ "$zfsrecursive" == "1" ]; then
    echo "Searching for recursive snapshots containing: ${zfsfolder} and ${zfslabel}"
    zfssnaplist=$(LC_ALL=C zfs list -H -t snapshot -o name,creation -s creation | grep "$zfsfolder" | grep "$zfslabel")
else
    echo "Searching for snapshots containing: ${zfsfolder}@ and ${zfslabel}"
    zfssnaplist=$(LC_ALL=C zfs list -H -t snapshot -o name,creation -s creation | grep "${zfsfolder}@" | grep "$zfslabel")
fi

if [ "$?" != "0" ]; then
    echo "Error: Could not get list of snapshots"
    exit $?
else
    echo "OK: List of snapshots was created"
fi

keepgen=$(echo $snapkeep | egrep '^g[0-9]+$')
keepday=$(echo $snapkeep | egrep '^d[0-9]+$')
keephour=$(echo $snapkeep | egrep '^h[0-9]+$')

if [ -n "$keepgen" ] && [ -n "$zfssnaplist" ]; then
    if [ "$zfsfolder" != "." ] && [ "$zfslabel" != "." ]; then
  zfsfolderlist=$(zfs list -t snapshot -o name | grep "$zfsfolder" | cut -d'@' -f1 | uniq)
  generations=$(echo "$snapkeep" | tr -d 'g')
  echo "Keep generations $generations was specified in parameter"

  for snapshot in $zfsfolderlist; do
      i=0
      #echo "test $snapshot"
      echo "$zfssnaplist" | sed -n '1!G;h;$p' | grep ${snapshot}@ |
      while read snapshot; do
   i=$((i+1))
   snapshotname=$(echo "$snapshot" | awk '{ print $1 }')
   if [ $i -gt $generations ]; then
       if [ "$dryrun" != "1" ]; then
        zfssnapdestroy=$(zfs destroy $snapshotname)
        if [ "$?" != "0" ]; then
            echo "Error: could not destroy snapshot $snapshotname"
        else
            echo "OK: snapshot $snapshotname entry $i was destroyed"
        fi
       else
        echo "Dryrun: snapshot $snapshotname entry $i was destroyed"
       fi
   else
       echo "Keep snaphot $snapshotname entry $i"
   fi
      done
  done
    else
  echo "Error: Either -F, -L was not specified, cannot determine generations from list"
  exit $?
    fi
elif ( [ -n "$keepday" ] || [ -n "$keephour" ] ) && ( [ -n "$zfssnaplist" ] ); then

    if [ -n "$keepday" ]; then
  alloweddays=$(echo "$snapkeep" | tr -d 'd')
  echo "Keep days $alloweddays was specified in parameter"
    elif [ -n "$keephour" ]; then
  allowedhours=$(echo "$snapkeep" | tr -d 'h')
  echo "Keep hours $allowedhours was specified in parameter"
    else
  echo "Error: could not get keep interval"
    fi

    echo "$zfssnaplist" |
    while read snapshot; do
  i=$((i+1))
  snapshotdate=$(echo "$snapshot" | awk '{ print $3 "-" $4 "-" $6 }' ) # get date of snapshot i.e. Dec-26-2014
  snapshothour=$(echo "$snapshot" | awk '{ print $5 }') # get time of snapshot i.e. 11:11

  getsnapdatesec=$(date +%s -d "$snapshotdate") #get age of snapshot date in seconds since 1970
  getsnaphoursec=$(date +%s -d "$snapshothour") #get age of snapshot time in seconds since 1970

  getdatenowsec=$(date +%s -d $(LANG=C date +%b-%d-%Y)) #get today date in seconds since 1970
  gethournowsec=$(date +%s -d $(LANG=C date +%H:%M)) #get today time in seconds since 1970

  getsnapagedate=$(( $getdatenowsec - $getsnapdatesec )) #get difference of today and snapday date = age of snapshot in days
  getsnapagehours=$(( $gethournowsec - $getsnaphoursec )) #get time difference of now and snapday time without full days between

  getsnapage_s=$(( $getsnapagedate + $getsnapagehours ))
  getsnapage_h=$(( $getsnapage_s / 3600 ))
  getsnapage_d=$(( $getsnapage_s / 86400 ))

  if [ -n "$alloweddays" ] && [ -n "$getsnapage_d" ]; then
      snapshotname=$(echo "$snapshot" | awk '{ print $1 }')
      if (( $getsnapage_d > $alloweddays )); then
   if [ "$dryrun" != "1" ]; then
       zfssnapdestroy=$(zfs destroy $snapshotname)
       if [ "$?" != "0" ]; then
        echo "Error: could not destroy $getsnapage_d days old snapshot $snapshotname from $snapshotdate $snapshothour"
       else
        echo "OK: $getsnapage_d days old snapshot $snapshotname from $snapshotdate $snapshothour was destroyed"
       fi
   else
       echo "Dryrun: $getsnapage_d days old snapshot $snapshotname from $snapshotdate $snapshothour was destroyed"
   fi
      else
   echo "Keep $getsnapage_d days old snapshot $snapshotname from $snapshotdate $snapshothour"
      fi
  elif [ -n "$allowedhours" ] && [ -n "$getsnapage_h" ]; then
      snapshotname=$(echo "$snapshot" | awk '{ print $1 }')
      if (( $getsnapage_h > $allowedhours )); then
   if [ "$dryrun" != "1" ]; then
       zfssnapdestroy=$(zfs destroy $snapshotname)
       if [ "$?" != "0" ]; then
        echo "Error: Could not destroy $getsnapage_h hours old snapshot $snapshotname from $snapshotdate $snapshothour"
       else
        echo "OK: $getsnapage_h hours old snapshot $snapshotname from $snapshotdate $snapshothour was destroyed"
       fi
   else
       echo "Dryrun: $getsnapage_h hours old snapshot $snapshotname from $snapshotdate $snapshothour was destroyed"
   fi
      else
   echo "Keep $getsnapage_h hours old snapshot $snapshotname from $snapshotdate $snapshothour"
      fi
  else
      echo "Error: Could not calculate snapshot age ( got $getsnapage_d days = $getsnapage_h hours)"
  fi
    done

else
    echo "Error: KEEP parameter $snapkeep missing or not valid or could not retrieve list of snapshots"
    exit $?
fi
 
Noch eine Sache: die vielen tee -a $blub kann man zu einem zusammenfassen, indem man alle Befehle in geschweifte Klammern setzt und dahinter das tee. Konkret:
Code:
{
befehl 1
befehl 2
befehl 3
} | tee -a $blub
Das vermeidet überflüssige Wiederholungen und dadurch entstehende Tippfehler.

Wenn man das macht geht innerhalb vom tee kein exit mehr, gibts da einen Workaround?
 
Wenn man das macht geht innerhalb vom tee kein exit mehr, gibts da einen Workaround?
Kannst du ein Beispiel geben, inwiefern "innerhalb vom tee" kein Exit mehr geht? Oder anders gefragt: Wo ist das "innerhalb", wenn tee doch nur ein simpler (externer) Befehl ist?

MfG Dalai
 
Code:
( 
{
echo "FEHLER"
exit 1 
} || exit 1 ) | tee -a $LOGFILE

das exti innerhalb {} beendet nur die abarbeitung der befehle innerhalb {}

besser wäre aber ne richtige funktion anzulegen dafür

Code:
myfunction () {
echo 1
ls  /verzeichnis1 || exit 1
echo 2
ls /verzeichnis2 || exit 1
echo 3
ls /verzeichnis3 || exit 1
}
....

( ( myfunction || echo "fehler verzeichnis nicht da"; exit 1 ) && echo erfolg) | tee -a $LOG
 
Zuletzt bearbeitet:
Achso ist das.

Code:
#!/bin/bash

test2() {
echo "123"
exit 1
echo "321"
}

test3() {
echo "555"
}

( ( test2 || echo "error"; exit 1 ) && echo "ok") | tee -a /tmp/test.tmp
( ( test3 || echo "error"; exit 1 ) && echo "ok") | tee -a /tmp/test.tmp

in dem Beispiel wird aber trotzdem 555 ausgegeben und auch kein "error" oder "ok"
 
Zuletzt bearbeitet:
in dem Beispiel wird aber trotzdem 555 ausgegeben und auch kein "error" oder "ok"
Logisch. Deine Funktion test3() enthält kein exit (mit Wert größer 0), also wird kein "echo error" gemacht. Danach geht der Fluss wohl weiter mit dem "exit 1", so dass kein "echo ok" ausgeführt wird.

Du kannst alternativ das Logging auch auslagern in eine Funktion, die du immer wieder aufrufst; ich mache das bei größeren Skripten so.

MfG Dalai
 
Eigentlich sollte der doch hier den exit machen:
( ( test2 || echo "error"; exit 1 ) && echo "ok") | tee -a /tmp/test.tmp
____________________^^^^^

Sobald ich den | tee wegmache funktioniert es jedoch.
 
Ich schätze, dass
Code:
test3 || echo "error"
zusammengehört und das Semikolon diese Befehlskette abschließt. Umgeschrieben sieht das doch so aus (jetzt mal unabhängig von den Subshells):
Code:
test3 || echo error
exit 1

Aber ich sehe gerade, dass du dich über das gleiche Verhalten der Befehlsketten wunderst, obwohl doch die eine Funktion mit Exitcode ungleich 0 verlassen wird, die andere aber nicht. Du kannst ja mal mit return rumprobieren. Ich würde solche komplexen Konstrukte jedenfalls vermeiden, denn das Verhalten kann keiner garantieren; vielleicht ändert es sich mal in einer späteren Version der Bash, und unabhängig von der Shell dürfte das auch nicht immer sein - gibt ja noch ein paar mehr Shells.

MfG Dalai
 
Ein return verlässt auch nur die sub.

"Aber ich sehe gerade, dass du dich über das gleiche Verhalten der Befehlsketten wunderst, obwohl doch die eine Funktion mit Exitcode ungleich 0 verlassen wird, die andere aber nicht."

Darum geht es nicht, die andere Sub soll überhaupt nicht gestartet werden. Der Exit funktioniert nicht (verlassen des Programms) wegen dem zusätzlichen Tee. Es ist also völlig egal was innerhalb test3 steht.

Wenn ich sowas in die erste Zeile packe dann bleibt das Script einfach stehen:
exec > >(tee -a /tmp/test.tmp)
exec > >(tee logfile)
 
Der Exit funktioniert nicht (verlassen des Programms) wegen dem zusätzlichen Tee.
Wieso? Er verlässt doch den gerade laufenden Kontext. Nur ist das ein anderer als du denkst, nämlich die Funktion bzw. die Subshell.

Ich glaube, es wäre besser, wenn du Exitcodes aus deiner Funktion zurückgibst und diese (auf der dann obersten Ebene) prüfst - dann kannst du auch das Skript verlassen. Und bedenke auch den Unterschied zwischen { } und ( ): die normalen Klammern starten eine Subshell, die geschweiften nicht.

MfG Dalai
 
Auf die Gefahr hin mich zu wiederholen ;D

Bash ist nicht zum Programmieren da, sondern eine shell.

Zum Skriptprogrammieren gibt es viel besseres wie zum Beispiel perl, python oder ähnliches.
Wo alles in eher kontrollierten Bahnen läuft, gegenüber einer Shell die ihr Verhalten nach einem Releasewechsel (Bashbleed etwa) ändert.
 
[OT]

Tom: Im Prinzip hast du ja recht, aber man lernt nicht mal eben schnell eine andere Sprache, nur weil sie besser geeignet ist. Das kann nämlich unter Umständen zu groben Anfängerfehlern führen, die schwere Konsequenzen haben. Klar, Steam zeigt(e) gerade, dass man auch bei vermeintlich simplen Dingen total versagen kann ;D.

[/OT]

MfG Dalai
 
Also gerade etwas wie Perl lernt man im Vorbeigehen.
Die Syntax ist einfach und relativ geläufig.
 
Nur ist das ein anderer als du denkst, nämlich die Funktion bzw. die Subshell.

Nein das ist nicht anders als ich denke, irgendwie verstehst du nicht was ich schreibe. Ein Exit ist normalerweise dazu da das Script vollständig zu verlassen und das tut es wegen den { } eben nicht. Das was tspoon oben gepostet hat, hätte das verlassen des Scripts nach der ersten Routine bewirkt aber funktioniert leider auch nicht.

Ich glaube, es wäre besser, wenn du Exitcodes aus deiner Funktion zurückgibst und diese (auf der dann obersten Ebene) prüfst

Irgendwie umständlich aber wenns nicht anders geht.

Und es ist nicht so das ich kein Perl könnte aber für ein paar Systembefehle ist das wie Kanonen auf Spatzen und bei z.B. Nas4free ist kein Perl installiert.
 
Code:
.... gelöscht


wenn du hinter || oder && mehre befehle machen willst muss das wieder in ()
exit 1 in funktionen scheint nicht zu funktionieren da muss return 1 genutzt werden, hatte ich jetzt aber nicht so ausgiebig getestet

aber irgendwie will er das exit 1 nicht haben, denkmal das wieder das problem mit den verschachtelten funktionen etc.

Code:
.... gelöscht

so auslöser in eindeutig das | tee was den exit des scriptes verhindert.

vllt hilft das hier hat einer das gleiche problem
http://stackoverflow.com/questions/1221833/bash-pipe-output-and-capture-exit-status


Code:
[COLOR=#000000]( ( false || ( echo "error"; exit 1) ) && echo "ok") [/COLOR]
-> error

Code:
( ( true || ( echo "error"; exit 1) ) && echo "ok")
-> ok

das funktioniert
 
Zuletzt bearbeitet:
Ein Exit ist normalerweise dazu da das Script vollständig zu verlassen
Wenn du mal in die Manpage schaust, wirst du lesen, dass eben genau das nicht notwendigerweise der Fall ist. Es wird die Shell beendet:
man bash schrieb:
exit [n]
Cause the shell to exit with a status of n. If n is omitted, the exit status is that of the last command executed.
Und im Falle normaler Klammern ist das eben die Subshell. Wir sind hier nicht bei Windows, wo ein exit in der Tat alles abbricht, sofern man nicht /b benutzt, um nur die gerade laufende Batch zu beenden.

Einfaches Testskript zur Demonstration:
Code:
#!/bin/bash

{
  echo sub1
  (
      echo sub2
      exit
      echo sub2
  )
  echo ende
}
Mach dir mal den Spaß und führ es aus, du wirst kein zweites "sub2" sehen. Und dann mach mal aus den normalen inneren Klammern geschweifte und du wirst kein "ende" mehr sehen, weil dann tatsächlich das Skript beendet wird (ist ja keine Subshell mehr da).

und das tut es wegen den { } eben nicht.
Na was nun? Oben war noch das tee schuld, nun sind's die geschweiften Klammern :].

MfG Dalai
 
Zuletzt bearbeitet:
es liegt an der kombination "funktion + tee" also an beiden

hier mal nen bsp was funktioniert:

Code:
false | tee /dev/null
[ $PIPESTATUS -eq 0 ] || exit $PIPESTATUS

wenn man das jetzt mit dem oben kombiniert:

Code:
test3 () {
# gehen mal davon aus das hier was nicht funktioniert
echo "555";
#return fehler mit status 1
return 1
}
## der befehl und fehler abfangen...
( ( test3 || ( echo "error"; exit 1) ) && echo "ok") | tee -a /tmp/test.tmp
##wenn fehler dann beenden
[ $PIPESTATUS -eq 0 ] || exit $PIPESTATUS
##
echo "text der nur ausgegeben wird wenn kein fehler vorliegt"


ausgabe _________________________

./test_funktion_exit.sh
555
error

ne andere lösung hab ich erstmal nicht gefunden
quelle: http://stackoverflow.com/questions/985876/tee-and-exit-status
 
Das obige funktioniert aber ich habe jetzt was ganz einfaches gefunden das auch geht:


exec > >(tee -a /tmp/test.tmp 2>&1)
 
Zurück
Oben Unten