Arash
Arash

Reputation: 3

how to fix 'too many arguments' error in bash script

i have problem with this part of code ; how can i solve it ? when i run this script error on line 6 ( first if statement )

#!/bin/bash
state=false
for i in 'systemctl list-units | grep .service | grep running | awk '{print $1}' | cut -d '.' -f 1'
do
    if [ $i = "cron" ]  
    then
        state=true
        break
    fi
done

if [ $state = "flase" ]
then
    echo " CRON Service is Stopped !" 
fi

i tried to change if [ $i = "cron" ] with end of ";" but not worked !

if [ $i = "cron" ]  
    then
    state=true
    break
fi

output is wrong ...

Upvotes: 0

Views: 1847

Answers (1)

Jason
Jason

Reputation: 2671

The error is in the set up in the for loop.

First this line

for i in 'systemctl list-units | grep .service | grep running | awk '{print $1}' | cut -d '.' -f 1'

Those single-quotes ( ' ) should be back-ticks ( ` ) to get any real result. Preferably, you should always use $(...) since it is a bit easier to read. This format also has identical meaning to the back-ticks. So this would give you the desired result:

for i in $(systemctl list-units | grep .service | grep running | awk '{print $1}' | cut -d '.' -f 1)

The reason for the error, is that you are sending the command (between the $(...)) into the for loop as a single string. So, you are looping through that string and not its output. When you get to the if statement, this is what it translates to:

if [ systemctl list-units | grep .service | grep running | awk {print $1} | cut -d . -f 1 = "cron" ]

Each space between the [...] is making up a new argument. Hence the error, "Too many arguments." As @oguzismail suggested, quoting $i is ideal and will avoid the error message, but you won't be getting the desired result.

Also, for future reference, a while read loop is usually better suited for this work. In this specific example, you will be okay because there should not be any spaces in the services. If there were spaces, you would need to do some $IFS shenanigans to get the for loop to behave the way you want where a while read loop would safely split on lines only. More information here.

Edit: There are a couple issues here I didn't notice before. I actually just noticed your use of true and false here (I am going to assume that the mispelling is a copy typo):

if [ $state = "flase" ]

true and false are actually programs (likely) referring to /usr/bin/true and /usr/bin/false that just return 0 and 1 respectively. As a matter of preference, I would write this line as:

if ! $state

Another issue you will have is here:

echo " CRON Service is Stopped !"

! is a special character used for history that will try to expand within double-quotes. To avoid, just use single quotes here:

echo ' CRON Service is Stopped !'

Now, that your error has been found, here is an alternative way to write this script. I split it into multiple lines for readability but you could put this all on one line:

#!/bin/bash

# This is the same as the original command with an additional grep -q (quiet).
# If the last grep finds the cron service, the return code ($?) is 0.
# The command after || only executes if the last command fails ($? not 0).
systemctl list-units |
  grep .service |
  grep running |
  awk '{print $1}' |
  cut -d '.' -f 1 |
  grep -q '^cron$' ||
  echo ' CRON Service is Stopped !'

Upvotes: 3

Related Questions