Jak sprawdzić, czy zmienna ma wiele niepustych linii w bash?

10

Powiedzmy, że mam dwie zmienne w bash:

MULTILINE="I have
more than one line"
SINGLE_LINE="I only have one line
"

Chcę wykryć, kiedy zmienna faktycznie zawiera więcej niż jeden wiersz tekstu, pomijając dodatkowe znaki nowego wiersza.

Więc to:

if [ some test on "$MULTILINE" ]; then echo 'yes'; else echo 'no'; fi

wydrukuje yes, a to:

if [ some test on "$SINGLE_LINE" ]; then echo 'yes'; else echo 'no'; fi

wydrukuje no.

W moim konkretnym przypadku nie sądzę, żebym musiał martwić się prowadzeniem pustych linii, ale nie zaszkodzi wiedzieć, jak to zrobić.

W jaki sposób mogę to zrobić?

jpmc26
źródło
@ krowe Dziękuję, ale czy możesz wskazać konkretne odpowiedzi, które ignorują końcowe puste linie? Nie widziałem żadnego. (Również odpowiednio zredagował tytuł.)
jpmc26

Odpowiedzi:

5

Najprostsze rozwiązanie, jakie znam:

if (( $(grep -c . <<<"$MULTILINE") > 1 ))

na przykład:

VAR="a
b"
if (( $(grep -c . <<<"$VAR") > 1 )); then
  echo VAR has more than one line
else
  echo VAR has at most one line
fi

==>

VAR has more than one line

Powyższe ignoruje wszystkie puste linie: wiodące, końcowe i wewnętrzne. Należy jednak pamiętać, że nie można mieć wewnętrznej pustej linii, chyba że istnieją co najmniej dwie niepuste linie, więc jej istnienie nie może zmienić pytania, czy po przycięciu początkowych i końcowych pustych linii jest więcej niż jedna linia.

rici
źródło
5
$ echo „$ MULTILINE” | wc -l
2)

$ echo „$ SINGLE_LINE” | wc -l
2)

$ echo „$ SINGLE_LINE” | sed -re '/ ^ $ / d' | wc -l
1

$ echo „$ MULTILINE” | sed -re '/ ^ $ / d' | wc -l
2)

Zobacz /programming/16414410/delete-empty-lines-using-sed,
aby uzyskać więcej informacji na temat przycinania / usuwania białych znaków i pustych linii za pomocą sed.

Teraz napisz swoje if expression ...użycie $( ... )w cudzysłowie, aby uzyskać liczbę wierszy i przetestować względem liczby:

if ["$ (echo" $ MULTILINE "| sed -re '/ ^ $ / d' | wc -l)" -gt 1]; następnie
  echo „więcej niż jedna linia”; 
jeszcze 
  echo „pojedyncza linia lub brak linii”; 
fi
Hannu
źródło
0

Powinna to zrobić niewielka modyfikacja tego kodu . Możesz umieścić go we własnym skrypcie do ponownego użycia w następujący sposób:

#!/bin/bash
nlhit=""
for (( i=0; i<${#1}; i++ )); do
    if [[ "${1:$i:1}" == $'\n' ]]; then
        nlhit="1"
    elif [[ "$nlhit" == "1" ]]; then
        exit 1
    fi
done

exit 0

Następnie możesz użyć go w ten sposób (zakładając, że nazwałeś poprzedni skrypt multiline-check.sh):

#!/bin/bash

EMPTYLINE=""
BLANKLINE="    "
ONLYLINES="


"

MULTILINE="I have
more than one line"
SINGLE_LINE="I only have one line
"
SECOND_LINE="
I begin with a newline"


echo -n "EMPTYLINE Check: "
multiline-check.sh "$EMPTYLINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "BLANKLINE Check: "
multiline-check.sh "$BLANKLINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "ONLYLINES Check: "
multiline-check.sh "$ONLYLINES"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "MULTILINE Check: "
multiline-check.sh "$MULTILINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "SINGLE_LINE Check: "
multiline-check.sh "$SINGLE_LINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "SECOND_LINE Check: "
multiline-check.sh "$SECOND_LINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi
krowe
źródło
Dostaję błąd składni za pierwszym razem fi. Niestety utknąłem na bash 3.1 (wersja msysgit). Nie widzę niczego, co wygląda jak błąd składniowy, ale oczywiście coś mi brakuje. Myśli?
jpmc26,
@ jpmc26 Wystąpił błąd. Zaktualizowałem go jako skrypt zewnętrzny, aby był łatwiejszy w użyciu.
krowe
@ jpmc26 Dodałem kilka dodatkowych testów, aby przetestować inne nieparzyste dane wejściowe.
krowe
0

Ignorowanie końcowych pustych linii

Oto podejście wykorzystujące awk:

echo "$TEST" | tac | awk 'f==0 && /./ {f=NR} END{if(f==NR){exit 0}; exit 1 }' && echo "Found A Single Line"

Jak to działa:

  • echo „$ TEST”

    Pobiera każdą zmienną powłoki, którą jesteśmy zainteresowani i wysyła ją do standardowego wyjścia.

  • tac

    To odwraca kolejność linii, tak że ostatnia linia jest zwracana jako pierwsza. Po wykonaniu taclinie końcowe stają się liniami wiodącymi.

    (Nazwa tacjest odwrotna catdo powodu, który tacrobi to, co catrobi, ale odwrotnie).

  • awk 'f==0 && /./ {f=NR} END{if(f==NR){exit 0}; exit 1 }'

    Przechowuje numer pierwszej niepustej linii w zmiennej f. Po przeczytaniu wszystkich liniach, porównuje fsię do całkowitej liczby linii NR. jeśli fjest równe NR, to mieliśmy tylko jedną linię (ignorując początkowe puste miejsca) i wychodzimy z kodem 0. Jeśli po pierwszej pustej linii była jedna lub więcej linii, to kończy się kodem `.

  • && echo "Found A Single Line"

    W przypadku awkwyjścia z kodem 0, echoinstrukcja jest wykonywana.

Ignorowanie zarówno wiodących, jak i końcowych pustych linii

Tworząc jedną dodatkową awkzmienną, możemy rozszerzyć test o ignorowanie zarówno początkowych, jak i końcowych pustych linii:

echo "$TEST" | awk 'first==0 && /./ {first=NR} /./ {last=NR} END{if(first==last){exit 0}; exit 1 }' && echo " Found A Single Line"

Ponieważ ta wersja awkkodu obsługuje zarówno początkowe, jak i końcowe spacje, tacnie jest już potrzebna.

Jednoczesne przyjmowanie awkkodu:

  • first==0 && /./ {first=NR}

    Jeśli zmienna firstma wartość zero (lub nie została jeszcze ustawiona), a linia ma znak, dowolny znak, to ustaw firstnumer linii. Po awkzakończeniu odczytu linii firstzostanie ustawiony na numer pierwszej niepustej linii.

  • /./ {last=NR}

    Jeśli linia ma jakiś znak, ustaw zmienną lastna bieżący numer linii. Po awkzakończeniu odczytu wszystkich linii zmienna będzie miała numer ostatniej niepustej linii.

  • END{if(first==last){exit 0}; exit 1 }

    Wykonuje się to po odczytaniu wszystkich wierszy. Jeśli firstjest równy last, to widzieliśmy zero lub wiersze niepustych linii i awkwychodzi z kodem 0. W przeciwnym razie kończy się kodem 1. Skrypt powłoki może jak zwykle przetestować kod wyjścia za pomocą ifinstrukcji lub &&lub ||.

John1024
źródło