[bash] skicka kommando-resultat till variabel ? + echofärger

Här pratar vi programmering i dessa olika former. Perl, C/C++, Pascal, ADA, Lisp, COBOL, ZX Basic och mm.
Post Reply
dot
Posts: 272
Joined: 17 June 2004, 18:54
Location: Värmdö
Contact:

[bash] skicka kommando-resultat till variabel ? + echofärger

Post by dot » 19 June 2004, 04:12

Nå, alltså, jag tänkte göra ett litet försiktigt bash-script som mountar
floppyn, ELLER om den är mountad redan, så avmountar den.

Man ska skriva a: bara, var det tänkt

Men problemet är att jag inte vet hur jag skickar vidare innehållet från
cat /etc/mtab|grep /dev/fd0 till en variabel som jag kan titta i.

Hur redirectar/forwardar/skickar jag vidare resultatet från ett
kommando till en variabel?


Så här långt har jag kommit hitills:

Code: Select all

#!/bin/bash

# här är de saker som ska mountas och var
DEV_PATH="/dev/fd0"
MOUNT_PATH="/mnt/floppy"

# skapa en variabel för resultatet från MTAB
# dvs, MTAB innehåller alla mountade enheter...
MTAB=""

# be om innehållet i MTAB, men bara de rader som har floppyns sökväg...
# skicka detta till variabeln $MTAB
cat /etc/mtab|grep $DEV_PATH >> $MTAB

# om floppyn INTE finns i mtab, som innehåller monterade prylar
if ["$MTAB" == ""]; then
#  montera då floppyn
   echo "Mounting floppydisk..."
   echo " - Device-path: $DEV_PATH"
   echo " - Mount-path : $MOUNT_PATH <-- Go HERE for the disk..."
   mount $DEV_PATH $MOUNT_PATH   
   echo "Use a: again to UNMOUNT the disk BEFORE you remove it!"
# eller om den existerar
else
#   avmontera
    echo "Unmounting floppydisk..."
    umount $MOUNT_PATH
fi
Vidare undrar jag hur man får färg när man använder echo?
(Har provat med escape-kommandon typ de man kör i PS1, men de ville inte)

EDIT: Man skriver...

Code: Select all

	# färger, svaga och LJUSA
	black='\e[0;30m';
	BLACK='\e[1;30m';

	red='\e[0;31m';
	RED='\e[1;31m';

	green='\e[0;32m';
	GREEN='\e[1;32m';

	yellow='\e[0;33m';
	YELLOW='\e[1;33m';

	blue='\e[0;34m';
	BLUE='\e[1;34m';

	magenta='\e[0;35m';
	MAGENTA='\e[1;35m';

	cyan='\e[0;36m';
	CYAN='\e[1;36m';

	white='\e[0;37m';
	WHITE='\e[1;37m';

	echo "${red}Röd text...";
	echo "${GREEN}Ljust grön text...";	
(Notera echo "${red}Röd text..."; - det går inte i detta fallet att skriva echo "$redRöd text..."; för det förvirrar bash, den kan inte skilja på variabel och text då)


Och en (antagligen) enkel, hur gör man radbrytning?
Last edited by dot on 21 June 2004, 04:25, edited 1 time in total.

User avatar
Jan Pihlgren
Posts: 1447
Joined: 22 April 2002, 02:00
Location: MÄRSTA
Contact:

Re: [bash] skicka kommando-resultat till variabel ? + echofä

Post by Jan Pihlgren » 19 June 2004, 05:23

dot wrote:
Men problemet är att jag inte vet hur jag skickar vidare innehållet från
cat /etc/mtab|grep /dev/fd0 till en variabel som jag kan titta i.
Raden cat /etc/mtab|grep $DEV_PATH >> $MTAB
ska förmodligen ändras till (men är inte säker)
MTAB=cat /etc/mtab|grep $DEV_PATH
Och en (antagligen) enkel, hur gör man radbrytning?
Använd "\n" , betyder "new line" som på svenska blir "ny rad"

mikma
Posts: 3349
Joined: 10 July 2003, 21:19

Re: [bash] skicka kommando-resultat till variabel ? + echofä

Post by mikma » 19 June 2004, 11:54

Jan Pihlgren wrote:...
Raden cat /etc/mtab|grep $DEV_PATH >> $MTAB
ska förmodligen ändras till (men är inte säker)
MTAB=cat /etc/mtab|grep $DEV_PATH
...
Den korrekta raden är

Code: Select all

MTAB=`cat /etc/mtab|grep $DEV_PATH`
/Mikael

dot
Posts: 272
Joined: 17 June 2004, 18:54
Location: Värmdö
Contact:

Post by dot » 19 June 2004, 21:02

Okej, kör i vind...

Code: Select all

#!/bin/bash

# här är de saker som ska mountas och var
DEV_PATH="/dev/fd0"
MOUNT_PATH="/mnt/floppy"

# skapa en variabel för resultatet från MTAB
# dvs, MTAB innehåller alla mountade enheter...
MTAB=""

# be om innehållet i MTAB, men bara de rader som har floppyns sökväg...
# skicka detta till variabeln $MTAB
MTAB=`cat /etc/mtab|grep $DEV_PATH`

# om floppyn INTE finns i mtab, som innehåller monterade prylar
if ["$MTAB" == ""]; then
#  montera då floppyn
   echo "Mounting floppydisk..."
   echo " - Device-path: $DEV_PATH"
   echo " - Mount-path : $MOUNT_PATH <-- Go HERE for the disk..."
   mount $DEV_PATH $MOUNT_PATH   
   echo "Use a: again to UNMOUNT the disk BEFORE you remove it!"
# eller om den existerar
else
#   avmontera
    echo "Unmounting floppydisk..."
    umount $MOUNT_PATH
fi 
Nu funkar det att mounta
Men jag får error när den unmountar på rad 16, dvs IF-satsen...

-> if ["$MTAB" == ""]; then

-> "line 16: /dev/fd0 /mnt/floppy vfat 0 0 rw No such file or directory"

Varför...? Försöker den göra sjuka saker i if-satsen? Den ska ju bara jämföra 2 strängar!? :o

EDIT: Svar (listade ut):
if ["$blabla"=""]; then <--- FEL
if [ "$blabla"="" ]; then <--- Rättare
... bash är känsligt ! (öhh... :roll: )

Och vad är skillnaden mellan " " och `` ?

Har nyss fattat att "$VARIABEL" skriver ut "Variabelns innehåll", och '$VARIABEL' skriver ut precis som det står... (dvs $VARIABEL).
Last edited by dot on 21 June 2004, 04:18, edited 3 times in total.

Pacman
Posts: 496
Joined: 13 October 2002, 13:21
Contact:

Post by Pacman » 20 June 2004, 11:59

Så här ser min lösning ut (skrev ihop det på någon minut, så inga kommenterar eller så):

Code: Select all

#!/bin/sh
                                                                                
DEV_PATH="/dev/cdrom";
MOUNT_PATH="/mnt/cdrom";
                                                                                
check=$(mount | grep "$DEV_PATH");
if [ "$check" = "" ]; then
    echo "Mounting $DEV_PATH to $MOUNT_PATH...";
    mount $DEV_PATH $MOUNT_PATH;
else
    echo "Unmounting $DEV_PATH...";
    umount $DEV_PATH;
fi
dot wrote: Och vad är skillnaden mellan " " och `` ?
Du använder "" när du tilldelar strängar värden, eller liknande. `` använder du om du vill köra ett kommando, dock tycker jag $() är ett snyggare sätt att göra det. Men några exempel:

Code: Select all

pacman@apollon testar $ echo "ls -l";
ls -l
pacman@apollon testar $ echo `ls -l`;
total 4 -rwxr-xr-x 1 pacman users 259 Jun 20 11:54 mount.sh
"Make 'emerge -u world', not 'rm -rf /'"

dot
Posts: 272
Joined: 17 June 2004, 18:54
Location: Värmdö
Contact:

Post by dot » 20 June 2004, 21:41

Åh, nu funkar det bättre :)

gillar också $() bättre än ``... lättare att skriva :P

Men fortfarande stoppas inte felmeddelandena, t ex om jag försöker
montera när det inte finns nåt i cd-rommen/floppyn.

Man trodde ju att detta skulle hamna i $check, alltså felmeddelandet, men
istället så printar den rakt ut... :roll:

Hur fångar man upp detta, så att t ex mount inte skriver ut nåt alls, utan
skickar allt den säger till $check, så jag kan formatera det som jag vill ?

Pacman
Posts: 496
Joined: 13 October 2002, 13:21
Contact:

Post by Pacman » 20 June 2004, 22:10

Här är ett exempel på vad du vill:

Code: Select all

#!/bin/sh
                                                                               
DEV_PATH="/dev/cdrom";
MOUNT_PATH="/mnt/cdrom";
                                                                               
check=$(mount | grep "$DEV_PATH");
if [ "$check" = "" ]; then
    echo "Mounting $DEV_PATH to $MOUNT_PATH...";
    debug=$(mount $DEV_PATH $MOUNT_PATH);
else
    echo "Unmounting $DEV_PATH...";
    debug=$(umount $DEV_PATH);
fi 
Utdatan finns nu i debug variabeln och du kan nu göra nödvändiga åtgärder därifrån. Tex anropa en funktion som kollar vilket felmeddelande det är och sen göra något utifrån det.
"Make 'emerge -u world', not 'rm -rf /'"

Kimon
Posts: 263
Joined: 23 August 2002, 08:06

Post by Kimon » 21 June 2004, 00:11

dot wrote:Man trodde ju att detta skulle hamna i $check, alltså felmeddelandet, men istället så printar den rakt ut...
Det beror på att bash endast använder stdout för att fylla variablen med data. Så här säger manualsidan för bash:
Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

Alltså, bash exekverar kommandot och tilldelar kommandots utdata till den variabel som står till vänster om likamedtecknet. Eventuella blankrader tas bort innan tilldelningen görs.

Observera orden standard output. I shelltermer så finns minst tre sk fildeskriptorer (filedescriptors). Fildeskriptorer är inget annat än filer (eller snarare pekare till filer) som kan användas för att läsa eller skriva data. I normalfallet så är minst tre av dessa öppna. En för läsning och två för skrivning. Den första av dessa kallas för standard input eller helt enkelt stdin. Normalt är stdin kopplad till ditt tangentbord och naturligtvis öppnad för läsning (vem vill skriva till sitt tangentbord?). Den andra är standard output som nämns i citatet ovan. Den kallas också stdout och är normalt kopplad till ditt terminalfönster och öppnad för skrivning. Stdout är radbuffrad. Dvs inget skrivs ut förrän raden avslutas med ett radbrytningstecken eller bufferten är full. Den tredje kallas standard error output eller stderr och är också den kopplad till ditt terminalfönster, men den är till skillnad från stdout obuffrad (tecken för tecken skrivs ut utan att vänta på radbrytningtecknet). Tanken är att stderr skall användas för att skriva ut felmeddelanden. Dessa vill man sällan ha buffrade utan se omedelbart när de inträffar. De tre fildeskriptorerna har också tre siffror. 0, 1, 2. Där stdin är 0, stdout är 1 och stderr är 2.

Ber om ursäkt för det långa utlägget om fildeskriptorer men det kan vara viktigt att känna till bakgrunden, om inte annat när man försöker läsa bash manualsida som minst sagt är kryckig.

Nå, om du kommer ihåg citatet, så används endast stdout för att tilldela variabeln. Troligen så skriver mount (som alla vettiga kommandon) ut sina fel på stderr.

Men misströsta inte, fildeskriptorer kan styras om att peka på andra filer i filsystemet eller andra fildeskriptorer. Det senare skall vi ägna oss åt i detta fall.

Så för att fånga upp felen som kommandot genererar och tilldela dessa till variabeln skall vi styra om stderr så att den skriver på samma fildeskriptor som stdout. Detta kallas i shelltermer för omstyrning (redirection). Kommandot för att göra det har du redan snuddat vid, nämligen '>'. Detta tecken talar om för bash att du vill ändra destinationen för en given fildeskriptor. Här skall fildeskriptorns tal (0,1,2) användas, men om man avser att styra om stdout kan talet utelämnas. Detsamma gäller för omstyrning av stdin, med tecknet '<', då stdins tal, dvs noll, kan utelämnas.

För att styra om en fildeskriptor skrivs: [n]>&[x] Där n är den fildeskriptor du önskar styra om från och där x är den fildeskriptor du önskar styra om till. x måste alltid anges, däremot kan n utelämnas om det är stdout enligt förklaring ovan.

Code: Select all

echo "test" >&2 /* Styr om stdout till stderr */
echo "test" 1>&2 /* Samma som ovanstående */
echo "test" 2>&1 /* Styr om stderr till stdout */
I ditt fall vill du alltså använda det andra exemplet 2>&1. Då kommer felmeddelanden att skrivas på stdout istället för på stderr. Variabeln kommer då att tilldelas även felmeddelandet.


//K

dot
Posts: 272
Joined: 17 June 2004, 18:54
Location: Värmdö
Contact:

Post by dot » 21 June 2004, 04:10

Lysande, tack :D

Prövade genast!

Men förstår inte mount fildeskriptorer, eller är det jag som är klantig?

Tänkte jag kunde lagra ev. fel i /tmp/.errors, men när jag kör...

Code: Select all

DEV_PATH="/dev/fd0";
MNT_PATH="/mnt/floppy";
ERR_PATH="/tmp/.errors";
...
mount $DEV_PATH $MNT_PATH 2&>$ERR_PATH
...så får jag bara hjälptexten i /tmp/.errors?

Har prövat att köra med 2&>1, men det ger samma resultat, fast i filen '1'.

Alltså jag får "Usage: mount <bla bla bla>"

Den mountar inte, den utför inget. Tar jag däremot bort dirigeringen av fel, ja då mountar den igen...

Här är hela koden:

Code: Select all

#!/bin/bash

# här är de saker som ska mountas och var
DEV_PATH="/dev/fd0";
MNT_PATH="/mnt/floppy";

# här lägger vi felen
ERR_PATH="/tmp/.errors";
NULL="/dev/null";
ERROR="";

# ta bort felfilen, vill inte veta om det gick fel
rm $ERR_PATH 2&>$NULL;

# plocka ut floppyn, OM den mountats förut,...
MOUNTED=$(mount|grep $MNT_PATH);

# om den inte mountats förut
if [ "$MOUNTED" = "" ]; then

   #  försök att montera floppyn
   echo "Mounting floppydisk...";
   


   # alt 1. FUNKAR INTE - försök att mounta, fel->felfilen
   mount $DEV_PATH $MNT_PATH 2&>$ERR_PATH;
   
   # alt 2. FUNKAR INTE - försök att mounta, fel->stdout
   # mount $DEV_PATH $MNT_PATH 2&>1;
   
   # alt 3. FUNKAR - försök att mounta, fel->stderr
   #mount $DEV_PATH $MNT_PATH;
   
   # hämta felen ur felfilen, om den är tom, fel->null
   ERROR=$(cat $ERR_PATH 2&>$NULL);
   
   # OM felfilen är tom
   if [ "$ERROR" = "" ]; then
   
     # berätta vad som skett - det har mountats
     echo " - Device-path: $DEV_PATH";
     echo " - Mount-path : $MNT_PATH";
     
   # ELLER om felfilen inte är tom
   else
     # skriv ut felet
     echo " - ERROR: $ERROR";
   fi
   
# eller om den existerar
else
#   avmontera
    echo "Unmounting floppydisk..."
    umount $MNT_PATH 2&>$ERR_PATH;
fi 
EDIT: Såg nu vilket pucko jag e - har vänt &> fel ! :roll: ... jaja, återkommer i ämnet (när jag sovit).

Tack iaf för tillfället.

Kimon
Posts: 263
Joined: 23 August 2002, 08:06

Post by Kimon » 21 June 2004, 11:33

Abahaaa....

Du vill att det som normalt skrivs på stderr, istället skall skrivas på en fil?

I så fall skall du inte använda ampersand ('&'). Ampersand säger till bash att den skall duplicera fildeskriptorn, vilket den måste göra när den skall skriva på en fil som redan är öppen. Konceptet med filduplicering kan förklaras i detalj om du så önskar, men vi sparar det till ett annat tillfälle.

Vill du styra om stderr till en fil skall du skriva: 2>filnamn.
Vill du styra om stderr till stdout skall du skriva 2>&1.

Eftersom exemplet i ditt skript relaterade till $(...) konstruktionen och eftersom den endast hanterar stdout så tänkte jag att att det var omstyrning av stderr till stdout du var ute efter.

1. mount $DEV_PATH $MNT_PATH 2&>$ERR_PATH
Nej, det här går ju inte eftersom du vill styra stderr till en fil. Då skall du inte använda '&'. Ampersand ('&') skall bara användas om du styr till en annan fildeskriptor.
Det borde alltså stå: mount $DEV_PATH $MNT_PATH 2>$ERR_PATH

2. mount $DEV_PATH $MNT_PATH 2&>1
Fungerar inte, eftersom '>' och '&' har blivit omkastade, som du mycket riktigt själv upptäckte.
Det borde alltså stå: mount $DEV_PATH $MNT_PATH 2>&1


Eftersom du inte är intresserad av att spara feltexten i en fil (egentligen) så tycker jag att du skall använda följande konstruktion:

Code: Select all

ERROR=$( mount $DEV_PATH $MNT_PATH 2>&1 )
Då kommer feltexten att hamna i $ERROR. Eftersom mount är tyst om det går bra så är ju detta ett utmärkt sätt att fånga feltexten. Då slipper du hela hanteringen med $ERR_PATH.

//K

User avatar
panzar
Posts: 100
Joined: 23 April 2003, 22:07

Post by panzar » 26 June 2004, 18:27

Kimon wrote:Fildeskriptor-förklaring.
//K
Får tacka så mycket för den detaljerade förklaringen. Jag använder fildeskriptorer imellanåt, men det dröjer alldeles för lång tid imellan så jag hinner alltid glömma av dom.
Jag sparade faktiskt ner din förklaring i en textfil som jag kan ha nära tillhands.

Tack,

Pär

Kimon
Posts: 263
Joined: 23 August 2002, 08:06

Post by Kimon » 27 June 2004, 16:32

panzar wrote:Får tacka så mycket för den detaljerade förklaringen.
Ah, nej det behövs inte. Det är bara roligt att få en möjlighet att dela med sig av kunskaper man kommit över med åren. Det som är svårt är att avgöra i hur stor detalj ett problem skall förklaras, för ofta får jag intrycket av att de som postar på forumet bara är intresserade av att få sitt problem löst och då få lösningen presenterad med så mycket text som behövs, men inte mer. Därför är jag något skeptisk till att dra iväg dessa långa utlägg eftersom det kanske inte är så intressant för den som ställer frågan.

Ett återkommande mönster i rätt många frågor rörande bash är att svaret oftast finns i manualen. Problemet är att manualen är skriven på så ämlans svårtydlig kansliengelska (vilket iofs är rätt) att den kan vara tämligen jobbig att sätta sig in i. Vill man exempelvis bara läsa om "redirection" blir det något jobbigt att förstå om man inte förstår koncepteten runt "filedescriptors", "brace expansion", "parameter expansion" etc. Koncepten i sig är inte svåra att förstå, men texten som förklarar dem är minst sagt asketisk och svårtydlig. Därmed skräms en del av den synbara komplexiteten i manualen, vilket är synd, för bashmanualen är en veritabel guldklimp.

Och dessutom så vill man inte förolämpa åhöraren med att börja förklara vad som händer när bash utför "command substitution" eftersom det rätt ofta kan vara svårt att avgöra hur insatt frågeställaren är i bash. Så man vill ju inte idiotförklara någon om det kan undvikas. Och som den här tråden visar, så är tarvar ju även en ganska liten (om ändock central) del av bash rätt mycket text för att förklaras på ett sätt som förhoppningsvis är någorlunda lätt att förstå.

Så för att egentligen förklara vad som händer vid "redirection" så måste man förklara, eller åtminstonde i förbigående nämna: "brace expansion", "tilde expansion", "parameter expansion", "command substitution", "arithmetic expansion", "quote removal", "pathname expansion" och "word splitting". Om alla dessa koncept skall förklaras så kommer det att ta en ganska lång stund och troligen kommer åhöraren att för länge sedan tappat intresset. Och då har man inte ens trampat i närheten av "duplication" när det gäller "filedescriptors" och vad som egentligen sker bakom kulissen vid användadet av '&' operatorn. För att inte tala om "here documents" och mer ovanliga former av omstyrning (typ öppna för read/write eller flyttandet av fildeskriptorer till höger och vänster. Sen kan jag ju inte alla dessa koncept på mina fem fingrar, utan rör mig snarare i domänen av att ha praktisk erfarenhet av dem. Och inte ens det i de alla av ovan uppräknade fall...

Så visst. Ibland skrämmer bashmanualen mig, men dessa skräckupplevelser blir alltmer sällsynta.

//K

Post Reply