Pętla przechodząca przez tabulowany plik w skrypcie bash

18

Oto co mam do tej pory:

#!/bin/bash
while read line; do
        DB=$(echo $line | cut -f1)
        USER=$(echo $line | cut -f2)
        PASS=$(echo $line | cut -f3)
        echo DB=$DB USER=$USER PASS=$PASS
done < users.txt

I próbka pliku wejściowego:

drupal_1    drupal1 tmmjXSWL
drupal_2    drupal2 FHiJSYHM
drupal_3    drupal3 b7bFNj06
drupal_4    drupal4 0AaV62EL

I dane wyjściowe ze skryptu:

DB=drupal_1 drupal1 tmmjXSWL USER=drupal_1 drupal1 tmmjXSWL PASS=drupal_1 drupal1 tmmjXSWL
DB=drupal_2 drupal2 FHiJSYHM USER=drupal_2 drupal2 FHiJSYHM PASS=drupal_2 drupal2 FHiJSYHM
DB=drupal_3 drupal3 b7bFNj06 USER=drupal_3 drupal3 b7bFNj06 PASS=drupal_3 drupal3 b7bFNj06

Z jakiegoś powodu każda zmienna jest ustawiona na całą linię. Kiedy używam echo users.txt | cut -f1z wiersza poleceń, token jest w porządku.

mortona42
źródło

Odpowiedzi:

6

Co powiesz na to?

$ awk '{print "DB="$1"\tUSER="$2"\tPASS="$3}' users.txt
DB=drupal_1 USER=drupal1    PASS=tmmjXSWL
DB=drupal_2 USER=drupal2    PASS=FHiJSYHM
DB=drupal_3 USER=drupal3    PASS=b7bFNj06
DB=drupal_4 USER=drupal4    PASS=0AaV62EL

Nie wiem, czy masz problem do rozwiązania, czy zastanawiasz się nad bardziej teoretycznym problemem.

bahamat
źródło
Dzięki, to działa. Próbuję napisać skrypt, aby stworzyć ponad 40 instalacji Drupal na serwerze dla klasy, którą prowadzę. Mam skrypty do konfiguracji baz danych i plików, to jest fragment skryptu, który połączy to wszystko. Problem z moim kodem polegał na tym, że linia $ zamieniała tabulatory w spacje podczas analizy. $(echo $line | cut -d" " -f1)działa również.
mortona42
kończy się niepowodzeniem, jeśli którekolwiek z pól zawierają spacje
v01pe
21

Problem leży w poleceniu echo $line. Ponieważ nie ma żadnych cudzysłowów $line, powłoka wykonuje na nim podział słów, a następnie interpretuje każde słowo jako wzór globowania. Wypróbuj to z

a='*.txt foo'
ls $a

W twoim przypadku zakładki oddzielają słowa, które następnie stają się osobnymi argumentami echo. echoPolecenie wypisuje argumenty oddzielone spacją. W cutrezultacie otrzymujemy pola rozdzielane spacjami zamiast pól rozdzielanych tabulatorami.

Zawsze umieszczaj podwójne cudzysłowy wokół podstawień zmiennych $fooi podstawień poleceń$(foo) (chyba że rozumiesz, dlaczego musisz je pominąć i dlaczego jest to w porządku). echo "$line"działałoby tutaj, ale jest to skomplikowany sposób robienia tego, co proponujesz.

Zachowując podejście do analizowania w powłoce, możesz sprawić, aby readpolecenie przeanalizowało dane wejściowe w polach.

while read DB USER PASS; do
  echo "DB=$DB USER=$USER PASS=$PASS"
done <users.txt

readdomyślnie dzieli pola rozdzielone znakami w wartości IFSzmiennej, która składa się ze spacji i tabulatora (oraz nowego wiersza, który nie może wystąpić wewnątrz linii). Aby podzielić tylko na kartach, IFSnajpierw ustaw jedną kartę. Pamiętaj, że początkowe i końcowe karty są ignorowane, a kolejne karty liczą się jako jedna karta.

readtraktuje \jako znak specjalny: odwrotny ukośnik, po którym następuje nowa linia, są ignorowane; \\staje się pojedynczym ukośnikiem odwrotnym. Jeśli chcesz uniknąć tego zachowania, podaj tę -ropcję.

Gilles „SO- przestań być zły”
źródło
15
+1. Aby ustawić IFS na znak tab w bash:IFS=$'\t'
glenn jackman