Bash scripts & χαρακτήρες newline

Πριν το βάψεις μπλε και το ρίξεις στη θάλασσα γιατί δεν ρίχνεις μια ματιά εδώ;

Συντονιστές: kostas213, markelos

Απάντηση
Άβαταρ μέλους
drcypher
Portal Administrator
Portal Administrator
Δημοσιεύσεις: 2300
Εγγραφή: Τετ Νοέμ 01, 2006 7:33 am
Real Name: Κώτσος Φίλ
Gender: Male
Τοποθεσία: Μπροστά στην οθόνη

Bash scripts & χαρακτήρες newline

Δημοσίευση από drcypher »

Προσπαθώ το τελευταίο δίωρο (μάλλον) να κάνω μια αρκετά απλή δουλειά μέσω ενός bash script. Μέρος της δουλειάς αυτής απαιτεί η έξοδος ενός προγράμματος (η οποία περιέχει τυχαίο αριθμό γραμμών με περιεχόμενα που δεν έχουν σημασία για τη συζήτηση) να "καθαριστεί" από whitespace (δηλ. πολλαπλά spaces, tabs, newlines, κλπ).

Με ένα απλό sed script μπόρεσα να καθαρίσω τα πάντα, εκτός φυσικά από το newline. Παραθέτω ένα παράδειγμα που δεν δουλεύει για να μην μπείτε στο ίδιο τριπάκι. Μπορείτε να τρέξετε την εντολή σε οποιονδήποτε κατάλογο έχει πάνω από ένα αρχείο (αλλιώς δεν θα έχετε newlines :P ):

Κώδικας: Επιλογή όλων

ls -1 | sed -e "s/\n/no new line/"
Το παραπάνω θα έπρεπε να παίρνει τη λίστα των αρχείων το ένα κάτω από το άλλο (αυτό κάνει η εντολή ls -1) και κατόπιν να αντικαθιστά τον χαρακτήρα αλλαγής γραμμής (newline ή \n) με την έκφραση "no new line". Δεδομένου, όμως, ότι το sed "επεξεργάζεται" το περιεχόμενο που του πλασάρετε γραμμή-προς-γραμμή, δεν πρόκειται ποτέ να βρείτε newline για να αντικαταστήσετε.

Σε όλη αυτή τη διαδικασία βρήκα και κάτι άλλο αξιοπερίεργο που με καθυστέρησε αρκετά από το να βρω τη λύση και αφορά την εντολή echo. Εφ' όσον το παραπάνω sed script δεν έχει αποτέλεσμα, ας κρατήσουμε μόνο την εντολή ls -1. Ουσιαστικά εγώ δούλεψα με μεταβλητές τις οποίες μετά έκανα echo, αλλά το ίδιο γίνεται και χωρίς την διαμεσολάβησή τους. Εκτελέστε την παρακάτω εντολή:

Κώδικας: Επιλογή όλων

echo "$(ls -1)"
Θα εμφανιστεί στην οθόνη σας η λίστα με τα αρχεία το ένα κάτω από το άλλο. Σαν να είχατε γράψει απλώς ls -1. Τρέξτε τώρα την παραπάνω εντολή χωρίς τα εισαγωγικά:

Κώδικας: Επιλογή όλων

echo $(ls -1)
Θα εμφανιστεί η λίστα με τα ίδια αρχεία, μόνο που δεν θα βρίσκονται το ένα κάτω από το άλλο αλλά το ένα δίπλα στο άλλο. Εννοείται, φυσικά, πως αν είχατε αποθηκεύσει τα παραπάνω σε μεταβλητές και προσπαθούσατε να κάνετε επεξεργασία (π.χ. με τους τελεστές ${#}, κλπ) θα είχατε πρόβλημα, καθώς τελικά τα newlines υπάρχουν αλλά στη μια περίπτωση "κρύβονται". Αν κάποιος γνωρίζει γιατί μπορεί να συμβαίνει αυτό (δηλ. αν φταίει το bash ή η echo) θα με ενδιέφερε πάρα πολύ.

Το αίσιο αποτέλεσμα της όλης ιστορίας (γιατί ήθελα να απαλείψω τα newlines) είναι τελικά πιο απλό απ' ό,τι περίμενα: Η εντολή tr, η οποία μπορεί να αφαιρέσει ή να αντικαταστήσει χαρακτήρες στο σύνολο των δεδομένων που το ταΐζετε (όπως υποθέτω με κάποιον τρόπο θα μπορεί να κάνει και το sed αν μπορεί κανείς να το βγάλει από το line-by-line mode). Η σωστή, λοιπόν, (είτε σε εμφάνιση στην οθόνη είτε σε αποθήκευση δεδομένων) γραφή είναι η παρακάτω:

Κώδικας: Επιλογή όλων

ls -1 | tr "\n" " "
Εύχομαι να μην παιδευτεί ποτέ κανείς ξανά με αυτό το πράγμα... δεν αξίζει :P
Από τούδε και στο εξής ως στρογγυλοί αριθμοί ορίζονται τα πολλαπλάσια του 5 και οι δυνάμεις του 2.
Άβαταρ μέλους
sparc
Δημοσιεύσεις: 391
Εγγραφή: Τετ Νοέμ 01, 2006 9:46 am
Real Name: Γιώργος
Gender: Male
Τοποθεσία: Ε204_κ.Φυσικής!!!

Re: Bash scripts & χαρακτήρες newline

Δημοσίευση από sparc »

Στην εντολή με το sed δοκίμασε να βάλεις τις επιλογές g ή i στο τέλος, όπως παρακάτω

Κώδικας: Επιλογή όλων

# sed -e 's/\n/no new line/i'
# sed -e 's/\n/no new line/g'
Δεν είμαι σίγουρος για την επεξήγηση αφού δεν είμαι σε unix αυτή τη στιγμή να δω το man, ωστόσο μπορώ να σου πω ότι και τα δύο αυτά flags αλλάζουν τον τρόπο που το sed δέχεται το input, ως προς το θέμα των πολλαπλών γραμμών.
Επίσης, όλες αυτές οι απορίες απαντώνται στα ακόλουθα, πλέον official, bash howtos:
Advanced Bash-Scripting Guide
Bash Guide for Beginners
Υπάρχουν και αρκετά άλλα στο The Linux Documentation Project

Το ίδιο πρόβλημα το είχα αντιμετωπίσει και εγώ σε αρκετά loop scripts και το έλυσα κάνοντας πρώτα rename τα αρχεία ώστε να μην έχουν κενά και μετά, τροφοδοτώ το for-loop με την λίστα των αρχείων από ένα απλό $(ls).
Το rename το έκανα με το ακόλουθο script (βάζει τελεία όπου υπάρχει κενό. Δεν προλαβαίνω να το τελειοποιήσω τώρα, αν υπάρχει είδη το target αρχείο θα τερματίσει με σφάλμα. Το ίδιο αν υπάρχουν περίεργοι χαρακτήρες όπως '#$%^&*" etc).Το pipe κάνει μερικά δικά του κολπάκια οπότε απαραίτητη είναι η πρώτη γραμμή όπως την βλέπεις, όσο χαζή και αν σου φαίνεται. (εγώ χρειάστηκα μέρες για να το ανακαλύψω αυτό το κολπάκι).

Κώδικας: Επιλογή όλων

#!/bin/bash
dr=$(echo -e $(ls -1b) |sed 's/\./\//g'| sed 's/\\ /./g')
for d in $dr;do
        n=$(echo $d|sed 's/\./\\ /g')
        n=$(echo $n|sed 's/\\ / /g')
        n=$(echo $n|sed 's/\//./g')
        d=$(echo $d|sed 's/\//./g')
        echo  mv \"$n\" \"./$d\"
done
Μια παραλλαγή θα ήταν να χρησιμοποιήσεις κάποιο μοναδικό συνδυασμό χαρακτήρων αντί για τελεία ώστε με αντίστροφη εφαρμογή των ανωτέρω να επαναφέρεις τα αρχεία.
Hope it helps ;)
I think therefore I am? Could be! Or is it really someone else who thinks he's me?
Reymond Smullyan - This book needs no title
Στενή είναι η αρετή, δεν μπορώ να αναπνεύσω· μικρός, στενός είναι ο Παράδεισος, δε με χωράει· σαν άνθρωπος μου φαίνεται ο Θεός σας, δεν τον θέλω!
Ν. Καζαντζάκης - Ασκητική
Άβαταρ μέλους
drcypher
Portal Administrator
Portal Administrator
Δημοσιεύσεις: 2300
Εγγραφή: Τετ Νοέμ 01, 2006 7:33 am
Real Name: Κώτσος Φίλ
Gender: Male
Τοποθεσία: Μπροστά στην οθόνη

Re: Bash scripts & χαρακτήρες newline

Δημοσίευση από drcypher »

Η αλήθεια είναι πως δεν προσπαθούσα να κάνω parse το output της ls αλλά άλλου προγράμματος. Επειδή, όμως, ανέφερες το ls και τον διαχωρισμό των αρχείων (ειδικά όταν αυτά περιέχουν spaces), η λύση είναι αρκετά πιο απλή. Αρκεί να αλλάξεις τη μεταβλητή $IFS (Internal Field Seperator). Στην αρχή το $IFS ισούται με space, tab και newline, με αποτέλεσμα αν βάλεις π.χ. κάτι σαν

Κώδικας: Επιλογή όλων

for i in $(ls -1)
και στη λίστα υπάρχουν αρχεία με spaces, να μην παίρνεις ολόκληρα τα ονόματα των αρχείων αλλά τα κομματάκια τους. Για να το αποφύγεις αλλάζεις τον IFS (ο οποίος χρησιμοποιείται για να διαχωριστούν τα στοιχεία στο for) ως εξής:

Κώδικας: Επιλογή όλων

IFS=$'\n'
Μου είχε φάει αρκετή ώρα να καταλάβω πως μπορείς να αποθηκεύσεις escape character σε μια μεταβλητή (γιατί εν αντιθέσει με το echo, όταν κάνεις ανάθεση το "\n" και το '\n' ερμηνεύονται κυριολεκτικά :( ).

Κοίταξα λίγο τα flags που λες για το sed αλλά μάλλον αφορούν τον τρόπο που θα κάνει replacements σε μια γραμμή (δηλ. αν θα το κάνει globally στη γραμμή, αν θα το κάνει μια-δυο φορές, κλπ). Θυμάμαι ότι το sed σου δίνει πρόσβαση στο σύνολο του buffer για να κάνεις δουλειά, αλλά δεν μπορώ να βρω πως το κάνει. Σε κάθε περίπτωση αφού βρήκα άκρη με το tr δεν θα το ψάξω άλλο.

Ευχαριστώ για τα pointers πάντως... Το bash όπως φαίνεται έχει πολύ περισσότερες δυνατότητες απ' ό,τι μπορούσα να φανταστώ (έχει μέχρι και random number generator)!
Off Topic
Α, και να μην ξεχάσω κάτι μου έκανε εντύπωση: Το strlen() σε bash για μια μεταβλητή π.χ. var γράφεται

Κώδικας: Επιλογή όλων

${#var}
Γαμάτο; 8)
Από τούδε και στο εξής ως στρογγυλοί αριθμοί ορίζονται τα πολλαπλάσια του 5 και οι δυνάμεις του 2.
dsimos
Δημοσιεύσεις: 92
Εγγραφή: Δευ Φεβ 19, 2007 7:57 pm
Real Name: Δημήτρης Σίμος
Gender: Male
Facebook ID: 0
Τοποθεσία: $HOST
Επικοινωνία:

Re: Bash scripts & χαρακτήρες newline

Δημοσίευση από dsimos »

Σωστος :D

Σε ενα παρομοιο προβλημα, εγω ειχα δουλεψει με την αντιστοιχη μεταβλητη του awk :P
Απάντηση

Επιστροφή στο “Υπολογιστές - Εφαρμογές - Internet”