bash script crasht (too many arguments)

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • amx
  • Registratie: December 2007
  • Laatst online: 22:42
Vanwege het kunnen crashen van mijn script door commando's op een read input in te voeren,
zoek ik een filter om spaties te herkennen.
Ondanks dat een functie met meerdere if .. then statements de read input verder goed filtert,
is onderstaande code bovenaan gezet en crasht het script nog.


code:
1
2
3
4
5
6
7
8
9
10
11
function foo
                   {
                   if
                   [  $INVOER == *\ * ]
                   then
                   echo "geen spaties toegestaan" && bar
                   fi
        
                   ...

                   }


Ik heb de code vergeleken met voorbeelden online, maar ik zie er geen fouten in.

De daaropvolgende fout tijdens draaien van het script is:
code:
1
2
3
4
5
<scriptnaam>: line 84: [: too many arguments
<scriptnaam>: line 91: [: too many arguments
<scriptnaam>: line 97: [: too many arguments
...
enz


De bedoeling is dat het script de volgende if then statements overslaat, en functie bar uitvoert.
Ik heb nagedacht om in een eerdere functie
code:
1
 sed 's/\ //g ' $INVOER
in te passen, nmaar ik zie niet goed hoe dit aan een "mooi geschreven'' programmacode bijdraagt (vergroot de feature creep)

Is er een fout in de eerdere code, of een handigere aanpak?

Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Too many arguments klinkt mij als iets waar je liever xargs voor wil gebruiken om het te spreiden over meerdere aanroepen, maar zonder iets meer van je script kan ik er verder weinig zinnigs over zeggen.

Hoe roep je het script aan?
Wat staat er op regel 84?

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • Kees
  • Registratie: Juni 1999
  • Laatst online: 02-10 10:44

Kees

Serveradmin / BOFH / DoC
met *\ * doe je alle files met een spatie erin listen, niet iets vergelijken, doe maar eens echo *\ *

wat je wil is:
code:
1
2
3
4
5
if [[ $INVOER =~ \  ]] # dat is dus matchen op "\ " - een geescapte spatie
then 
    echo "geen quote"
    bar
fi

Als je het overigens hebt over "mooi geschreven" code, dan zou ik 'echo "geen spaties toegestaan" && bar' zelf nooit doen, waarom die &&? mag hij die bar niet doen als een echo failed? (kan dat uberhaubt wel?)

"Een serveradmin, voluit een serveradministrator, is dan weer een slavenbeheerder oftewel een slavendrijver" - Rataplan


Acties:
  • 0 Henk 'm!

  • amx
  • Registratie: December 2007
  • Laatst online: 22:42
Als de oorzaak is dat de if...then in dubbele brackets moet staan,
snap ik de aanleiding

Het werkt inmiddels in dit script
Mijn gok is als ik vroeger beter bij scheikunde had opgelet, ging het inzicht krijgen waarom de inkapseling nu het script niet meer laat crashen wel duidelijker

Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 20:04

deadinspace

The what goes where now?

amx schreef op donderdag 04 juli 2013 @ 15:26:
Vanwege het kunnen crashen van mijn script door commando's op een read input in te voeren,
zoek ik een filter om spaties te herkennen.
Dat is geen goede aanpak eigenlijk... Als je script de mist in gaat als je hem spaties voert, dan kun je beter dat fixen in plaats van spaties te verbieden :)

Spaties en quoting goed doen in bash kan een beetje tricky zijn, zeker voor beginners, maar het kan zeker wel, en dus is er eigenlijk geen excuus voor shell scripts om te struikelen over spaties ;)
code:
1
if [  $INVOER == *\ * ]
Allereerst gaat het vergelijken met strings met een enkel isteken, geen dubbele. Zie ook man test.

Verder doet *\ * niet wat je denkt dat het doet. * is een file-globbing teken dat willekeurige stukken filename matcht. Niet willekeurige stukken string. Als in je huidige directory de bestanden "bla bla.txt", "nou zeg.txt" en "jaja.txt" staan, dan wordt *\ * vervangen door bla bla.txt nou zeg.txt (wat niet is wat jij wil en makkelijk tot "test: too many arguments" kan leiden)

En als laatste: $INVOER wordt "letterlijk" vervangen door de inhoud van $INVOER. Als $INVOER dus "hoi doei" bevat, dan wordt jouw regel bash eigenlijk:
if [ hoi doei == *\ * ]

Maar dat zijn 4 argumenten ("hoi", "doei", "==" en "*\ *")! Niet wat test wil. Dat moet je tegengaan door $INVOER te quoten (ook wel: escapen), zo:
if [ "$INVOER" == *\ * ]

Wat dan vervolgens dit wordt:
if [ "hoi doei" == *\ * ]

En dat is wel wat je wil! Nouja, afgezien van het dubbele isteken en de *\ * ;)

Ik gok dat je originele probleem (je script crasht op invoer met spaties) eenzelfde oorzaak heeft, en dus ook op te lossen is met correcte quoting.

Leesvoer:
http://wiki.bash-hackers.org/syntax/quoting
http://mywiki.wooledge.org/BashPitfalls
http://mywiki.wooledge.org/Quotes

Acties:
  • 0 Henk 'm!

  • amx
  • Registratie: December 2007
  • Laatst online: 22:42
Met quotes bleef er de stderr in het script, maar dan niet op meerdere regels, maar alleen op
de regel
code:
1
 [ -z $INVOER ]


Quotes invoegen lost het gedeelte op dat de invoer als een enkele string wordt gelezen,
pas met de dubbele brackets is de code voldoende ingekapseld om in het rest van het script geen fouten te veroorzaken.

Het is nog een beetje trial & error

Dank voor de links, ik zal ze zo nog eens doorlezen

[edit]
Kees schreef op donderdag 04 juli 2013 @ 18:22:
met *\ * doe je alle files met een spatie erin listen, niet iets vergelijken, doe maar eens echo *\ *

Als je het overigens hebt over "mooi geschreven" code, dan zou ik 'echo "geen spaties toegestaan" && bar' zelf nooit doen, waarom die &&? mag hij die bar niet doen als een echo failed? (kan dat uberhaubt wel?)
De tekst na "then" is niet zoals het in het daadwerkelijke script staat.
Ik had ook een punt-komma kunnen gebruiken. Echo is een programma dat in C geloof ik fprint in een bepaalde constructie uitvoert. Het is wel mogelijk dat echo mislukt, al heb ik het nog nooit gezien.

Ik denk dat zodra je C code in je script erbij voegt, je wel wat problemen tegen zal komen als je je best doet

[ Voor 48% gewijzigd door amx op 05-07-2013 12:00 ]


Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 20:04

deadinspace

The what goes where now?

amx schreef op vrijdag 05 juli 2013 @ 11:47:
Met quotes bleef er de stderr in het script, maar dan niet op meerdere regels, maar alleen op
de regel
code:
1
 [ -z $INVOER ]


Quotes invoegen lost het gedeelte op dat de invoer als een enkele string wordt gelezen,
pas met de dubbele brackets is de code voldoende ingekapseld om in het rest van het script geen fouten te veroorzaken.
Hoe bedoel je dat?
if [ -z "$INVOER" ]

zou namelijk gewoon moeten werken, zonder foutmeldingen.


Merk op dat er wel meer verschillen zijn tussen [ en [[, zie http://mywiki.wooledge.org/BashFAQ/031 . Merk ook op dat dat niet portable meer is en dat je dus #! /bin/bash boven je script zet en niet #! /bin/sh ;)

(Overigens kan het, afhankelijk van wat je probeert te doen, handiger zijn om zijn om een echte programmeertaal te gebruiken in plaats van shell)

Acties:
  • 0 Henk 'm!

  • Rainmaker
  • Registratie: Augustus 2000
  • Laatst online: 14-07-2024

Rainmaker

RHCDS

deadinspace schreef op vrijdag 05 juli 2013 @ 10:47:

Maar dat zijn 4 argumenten ("hoi", "doei", "==" en "*\ *")! Niet wat test wil. Dat moet je tegengaan door $INVOER te quoten (ook wel: escapen), zo:
if [ "$INVOER" == *\ * ]

Wat dan vervolgens dit wordt:
if [ "hoi doei" == *\ * ]

En dat is wel wat je wil! Nouja, afgezien van het dubbele isteken en de *\ * ;)
Je vergeet hier nog 1 edge-case, wat volgens mij ook iets is waar de TS tegenaan loopt; aangezien $INVOER letterlijk vervangen wordt, kan $INVOER ook zijn.
Dus, dan ziet bash het volgende:
if [  == ... ]

Waardoor het ook geen geldige vergelijking is en iets krijgt als "[: unary operator expected" (uit mn hoofd).

De oplossing is om $INVOER tussen quotes te zetten, zodat je dit krijgt:
if [ "" == ...]

Dit is wel geldig.

We are pentium of borg. Division is futile. You will be approximated.


Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 20:04

deadinspace

The what goes where now?

Rainmaker schreef op zaterdag 06 juli 2013 @ 01:06:
De oplossing is om $INVOER tussen quotes te zetten, zodat je dit krijgt:
Ja, dat zeg ik toch ook? ;)

Acties:
  • 0 Henk 'm!

  • sam.vimes
  • Registratie: Januari 2007
  • Laatst online: 08-06 08:44
amx schreef op donderdag 04 juli 2013 @ 15:26:
Vanwege het kunnen crashen van mijn script door commando's op een read input in te voeren,
zoek ik een filter om spaties te herkennen.
...
code:
1
2
3
4
5
6
7
8
9
10
11
function foo
                   {
                   if
                   [  $INVOER == *\ * ]
                   then
                   echo "geen spaties toegestaan" && bar
                   fi
        
                   ...

                   }

...
In eerdere antwoorden zijn al een hoop goede tips gegeven, maar het correcte gebruik van quoting heb ik nog niet gezien.

Je moet zorgen dat links en recht van de == operator precies 1 argument staat. Dat doe je door ze te quoten. Gebruik dubbele quotes als $-expressies geëxpandeerd moeten worden; enkele quotes als dat niet moet. $INVOER moet duidelijk geëxpandeerd worden.
Bash:
1
2
3
4
if [ "$INVOER" == '* *' ]
then
  echo "geen spaties toegestaan"
fi

Als de == operator wordt gebruikt, wordt er rechts een matchpatroon verwacht. Zie man bash onder Pattern Matching. Maar dat patroon moet je beschermen tegen filename expansion, vandaar de quotes (enkel of dubbel maakt niet uit, het is geen $-expressie).
Pagina: 1