this bash one-liner is beyond me

Giorgos Keramidas keramida at ceid.upatras.gr
Wed Jan 11 17:19:59 EET 2006


On 2006-01-11 16:53, "Nick Demou (enLogic)" <ndemou at enlogic.gr> wrote:
> στην σελίδα [1] βρήκα ένα bash one-liner το οποίο επιτρέπει σε ένα
> χρήστη να εκπαιδευει το spamassassin (κάνωντας move τα  email που έχουν
> κακοχαρακτηριστεί ως spam ή ham σε δύο άλλους φακέλους -it_is_spam ,
> it_is_ham-). Φυσικά δεν δούλεψε :-) και ψάχνωντας κατέληξα σε αυτό το
> κομάτι της γραμμής το οποίο "χτυπάει" (όλα μια γραμμή):
>
> $/usr/bin/tee >   \
> (/usr/bin/sa-learn --spam --single > /dev/null)   \
> | /usr/bin/spamc | /usr/lib/cyrus-imapd/deliver Inbox
>
> αυτό που θεωρητικά θα έπρεπε να κάνει είναι να πάρει το stdin και να το
> προωθήσει αφενώς στην εντολή
>    /usr/bin/sa-learn --spam --single > /dev/null
>
> και αφετέρου στην εντολή
>    | /usr/bin/spamc | /usr/lib/cyrus-imapd/deliver Inbox
>
> αν το τρέξεις όμως το bash παραπωνιέται:
>    bash: syntax error near unexpected token `('
>
> εγώ κατέθεσα τα όπλα (ίσως και να έχω κάψει νευρώνες βεβαιά[2]) - καμιά
> ιδέα?

Νευρώνες έχει κάψει ο καημένος που το έγραψε και πρέπει να τον χτυπήσεις
στο κεφάλι με printout από το Single UNIX Specification v.3, για να
μάθει να μη γράφει bash-specific μαλακίες και μάλιστα λάθος.

Το > redirection δεν κάνει αυτό που ελπίζει αυτός να κάνει, δηλαδή να
στείλει το output της tee ως stdin στην εντολή που υπάρχει ανάμεσα από
τα ( ... ).  Στην προσπάθεια να αποφύγει να φτιάξει `temporary file'
κατέληξε σε μια μπούρδα και μισή.

Μια λύση, που όμως _χρησιμοποιεί_ temporary file, είναι μάλλον κάτι λίγο
πιο περίπλοκο, που μοιάζει με αυτό:

    env TMPFILE=`mktemp /var/tmp/saXXXXXX` \
        ( [ -z "${TMPFILE}" ] && exit 1 ; cat > "${TMPFILE}" ;
          { /usr/bin/spamc "${TMPFILE}" ; spam=$? ; } | \
          /usr/lib/cyrus-imapd/deliver Inbox ; \
          type="spam" ; \
          [ $spam -eq 0 ] && type="ham" ; \
          /usr/bin/sa-learn --"$type" --single < "${TMPFILE}" >/dev/null 2>&1 ; \
          /bin/rm -f "${TMPFILE}" )

Αν αυτό δεν έχει τον περιορισμό να είναι ``μια γραμμή'', μπορείς να το
κάνεις κανονικό script και να γράψεις πιο ξεκάθαρο κώδικα και να βάλεις
σε καίρια σημεία σχόλια σχετικά με τα βήματα που κάνεις:

        #!/bin/sh

        TMPDIR="${TMPDIR:-/var/tmp}"
        TMPFILE=`mktemp "${TMPDIR}/saXXXXXX"`

        if test $? -ne 0 || test -z "${TMPDIR}" ; then
                echo 2>&1 "spam-filter [$$]: ${TMPDIR}: failed to create temporary file"
                exit 75
        fi

        cat > "${TMPFILE}"
        if test $? -ne 0 ; then
                echo 2>&1 "spam-filter [$$]: ${TMPFILE}: cannot save message text"
                exit 75
        fi

        # * NOTE: We run spamc in the current shell/process to be able
        # to save $rc with its return code in the environment of the
        # spam-filter process.  Saving the return code of cyrus-imapd is
        # not exactly what we want to do, since spamc may report a
        # message as "ham", but failing to deliver it may trigger a
        # "spam" result because of the failure reported by the entire
        # pipe command.

        { /usr/bin/spamc "${TMPFILE}" ; spam=$? ; } | /usr/lib/cyrus-imapd/deliver Inbox
        if test $? -ne 0 ; then
                echo >&2 "spam-filter [$$]: ${TMPFILE}: failed to deliver message to cyrus imapd"
                exit 75
        fi

        # Feed the message once more, to sa-learn, since we know the
        # result of the checks by spamc by now.

        if test $spam -eq 0 ; then
                type="ham"
        else
                type="spam"
        fi

        /usr/bin/sa-learn --"$type" --single < "${TMPFILE}" > /dev/null 2>&1

        # It's ok to ignore errors here.  The worst that can happen is
        # that sa-learn will learn slower.

        /bin/rm -f "${TMPFILE}"
        exit 0





More information about the Linux-greek-users mailing list