I'm trying to create a simple shell script to list the first input 6 times, a line, then report the size of the second input. Here is my script:
#!/bin/sh
# script1.sh
#
#
# $1=filename $2=number
i=0
while [$i -lt 7] #line 11
do
i=$(($i + 1))
echo $1
done
printf "\n"
if [$2 -gt 1000] #line 19
then
echo 'This is a big number!'
else
echo 'This is a small number.'
fi
Here is the error I receive when trying to use:
./script1.sh test 131234
./script1.sh: line 11: [0: command not found
./script1.sh: line 19: [131234: command not found
This is a small number.
I suppose it partially works but something about the command -lt and -gt is causing an error. Running on both Linux and Terminal (OS X) provide the same error.
You need spaces on your [] condition:
if [ $2 -gt 1000 ] #line 19
^ ^
and
while [ $i -lt 7 ] #line 11
^ ^
instead of
if [$2 -gt 1000] #line 19
and
while [$i -lt 7] #line 11
Note that otherwise it will not consider these expressions as it should. Instead, it understand them as a string called [$2 or [$i.
You need spaces around []:
while [ $i -lt 7 ] #line 11
And the other lines with [].
[ is a command like anything else, so when you use [$i, it's trying to execute [$i, in this case [0 and [121234.
$ \[.exe --help
Usage: test EXPRESSION
or: test
or: [ EXPRESSION ]
or: [ ]
or: [ OPTION
Exit with the status determined by EXPRESSION.
--help display this help and exit
--version output version information and exit
An omitted EXPRESSION defaults to false. Otherwise,
EXPRESSION is true or false and sets exit status. It is one of:
( EXPRESSION ) EXPRESSION is true
! EXPRESSION EXPRESSION is false
EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true
-n STRING the length of STRING is nonzero
STRING equivalent to -n STRING
-z STRING the length of STRING is zero
STRING1 = STRING2 the strings are equal
STRING1 != STRING2 the strings are not equal
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2
FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2 FILE1 is older than FILE2
-b FILE FILE exists and is block special
-c FILE FILE exists and is character special
-d FILE FILE exists and is a directory
-e FILE FILE exists
-f FILE FILE exists and is a regular file
-g FILE FILE exists and is set-group-ID
-G FILE FILE exists and is owned by the effective group ID
-h FILE FILE exists and is a symbolic link (same as -L)
-k FILE FILE exists and has its sticky bit set
-L FILE FILE exists and is a symbolic link (same as -h)
-O FILE FILE exists and is owned by the effective user ID
-p FILE FILE exists and is a named pipe
-r FILE FILE exists and read permission is granted
-s FILE FILE exists and has a size greater than zero
-S FILE FILE exists and is a socket
-t FD file descriptor FD is opened on a terminal
-u FILE FILE exists and its set-user-ID bit is set
-w FILE FILE exists and write permission is granted
-x FILE FILE exists and execute (or search) permission is granted
Except for -h and -L, all FILE-related tests dereference symbolic links.
Beware that parentheses need to be escaped (e.g., by backslashes) for shells.
INTEGER may also be -l STRING, which evaluates to the length of STRING.
NOTE: [ honors the --help and --version options, but test does not.
test treats each of those as it treats any other nonempty STRING.
NOTE: your shell may have its own version of test and/or [, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports.
Report [ bugs to bug-coreutils#gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils '[ invocation'
Work with Cygwin+Bash, should work in any environment ,IMHO.
Related
I am trying to check if every line in a file matches my pattern (4 characters followed by 4 digits). I tried using GREP with -x -P -v -q options so it returns 1 if my file doesn't match the requirements. I expect it to return nothing in case the file is correct, but it returns nothing even if the file has an error.
$4 is my input file.
My code is:
if [ -f $4 ] && [ `grep -q -P -x -v [[a-z]x{4}/[\dx{4}] $4` ]
then
echo "error"
exit 1
fi
input example:
bmkj2132
ahgc3478
(no uppercase)
Don't check the output of grep, check its exit code.
if [ -f "$4" ] && grep -q ...
Note the double quotes around $4 - otherwise a file name containing whitespace will break the script.
Also, single quote the regex. Square brackets are special in bash and you don't want the regex to suddenly change (expand) when a random filename exists.
Also note that x doesn't mean "times" (under -e, -E, -P neither). The quantifier just follows the quantified with no operator in between:
echo abcd1234 | grep -xP '[a-z]{4}\d{4}'
So, the full condition should be
if [ -f "$4" ] && grep -qvxP '[a-z]{4}\d{4}' "$4" ; then
echo Error >&2
exit 1
fi
Are you sure you want to continue if the file doesn't exist? If not, change the condition to
if ! [ -f "$4" ] || grep ...
BTW, you don't really need the PCRE expression here. If you replace \d by [0-9] or [[:digit:]], you can switch to -E (extended regular expression).
The point about using the exit code of grep is that it does not match the required condition.
Try the following:
if [ -f "$4" ] ; then
wronglines=$(grep -v pattern "$4")
if [ "$wronglines" = "" ] ; then
echo "all is well"
else
echo "a wrong line in the file"
fi
else
echo "cannot even find the file"
fi
This answer describes how we can use grep to search for any line not matching a particular regex pattern. We can modify this to provide a one-liner.
grep -Evq "[1-2]" file.txt && echo "error" && exit 1 || true
On the following file.txt, the error message and exit code will be triggered:
1
2
3
Without || true, this will always have an false return code. Additionally, this only works for the specified use case; modifying exit 1 to something like true will break the one-liner.
The regex pattern included within this example should be modified to the desired pattern.
I use bash, I have to read a file that contains many lines
Each line is composed by a single number. Then, for each line (number) I have to check if value > 80.
And here's my problem, no matter what I try, I always get:
>integer expression expected
after the if condition. That's because the variable I use to put the value of the line is a string.
Here's my original file
[root#michelep_centos2 ~]# cat temp1
0
0
0
98
0
0
79
0
81
In the bash script I have
##!/bin/bash
file=/root/temp1
while IFS= read -r line
do
if [ 'expr $line /1' -gt 80 ]
then echo "hit>80"
fi
done <"$file"
And here expr returns error as $line is a string. I have tried using another variable
val=$(($line + 0))
if [ $val -gt 80 ]
Here the if condition returns "integer expression expected"
I have used also echo
val=$(echo "$((line /1))")
if [ $val -gt 80 ]
I get
syntax error: invalid arithmetic operator (error token is ...
from echo command and of course again the if condition returns
integer expression expected
First action: dos2unix -f inputfile
Second action:
From the input file it can be observed that it contains blank lines, and this will cause if comparison to fail. You can put an additional check on top of your -gt 80 check to ensure before passing the variable $line its not empty using if [ ! -z $line ] or better you can put a check to ensure the line is an integer or may be both using AND.
Example:
while read line;
do
if [[ $line =~ ^[0-9]+$ ]];then
if [ $line -gt 80 ];then
echo "$line is greater then 80"
fi
fi
done <input_file
Or , if you do not want to put empty check([ ! -z $line ]) ,
Also, this can be done using other tools like awk in one line, but this may or may not fit in your requirement.
awk 'NF && $0>80{print $0 ,"is greater then 80"}' inputfile
Try this Shellcheck-clean pure Bash code:
#! /bin/bash -p
file=/root/temp1
while read -r line ; do
(( line > 80 )) && echo "$line > 80"
done <"$file"
The problem with the code in the question is that it doesn't handle empty lines. This code does handle empty lines because variables containing the empty string are treated as if they contained zero in arithmetic expressions (((...))).
I am trying to create a command which lists the first or last n lines (number specified by the user) of every file in a directory which is also specified by the user. They also have the option of using head or tail.
HEADORTAIL=$1
NUMLINES=$2
DIRECTORY=$3
if [ $# -lt 3 ]
then
echo "The command needs three arguments to work"
echo "The usage of this command is as follows: lshead [-head or -tail] [n$
exit
elif [ -d "$DIRECTORY" ]
then
echo "This directory exists"
while [ $2 -lt 1 ] ; do
echo "The number of lines you wish to see must be greater than 0"
read $2
done
if [ $1 == "-head" ]
then
head -$2 $HOME/$3/*
elif [ $1 == "-tail" ]
then
tail -$2 $HOME/$3/*
exit
fi
fi
fi
fi
exit
I don't really know what to expect in terms of an answer from the terminal. Apologies if I have made any mistakes or done anything stupid. I am relatively knew to bash shell scripting and to this site.
I get this error when I run the command.
lshead3 -head 10 bin
./lshead3: line 30: syntax error near unexpected token `fi'
./lshead3: line 30: `fi'
Just got this when I put it into ShellCheck
$ shellcheck myscript
Line 9:
echo "The usage of this command is as follows: lshead [-head or -tail] [n$
^-- SC1009: The mentioned parser error was in this simple command.
^-- SC1078: Did you forget to close this double quoted string?
Line 11:
elif [ -d "$DIRECTORY" ]
^-- SC1079: This is actually an end quote, but due to next char it looks suspect.
^-- SC1078: Did you forget to close this double quoted string?
Line 13:
echo "This directory exists"
^-- SC1079: This is actually an end quote, but due to next char it looks suspect.
Line 21:
elif [ $1 == "-tail" ]
^-- SC1073: Couldn't parse this double quoted string.
Line 28:
^-- SC1072: Expected end of double quoted string. Fix any mentioned problems and try again.
$
1 #!/bin/bash
2 head_or_tail=$1
3 num_lines=$2
4 directory=$3
5
6 if [ $# -ne 3 ]
7 then
8 echo "The command needs three arguments to work"
9 echo "The usage of this command is as follows: lshead [head or tail] [numlines] [directory]"
10 exit 1
11 fi
12
13
14 if [[ "$num_lines" -lt 1 ]]
15 then
16 echo "The number of lines you wish to see must be greater than 0"
17 exit 1
18 fi
19
20 if [[ ! -d "$directory" ]]
21 then
22 echo "Directory $directory does not exist"
23 exit 1
24 else
25 echo "looking in directoy $directory"
26 if [[ "$head_or_tail" = "head" ]]
27 then
28 head -n "$num_lines" "$directory"/*
29 elif [[ "$head_or_tail" = "tail" ]]
30 then
31 tail -n "$num_lines" "$directory"/*
32 fi
33 fi
34 exit
NOTE:
Line 2-4: You store your command line arguments into variables, so that you use them downwards, and keep your code readable and easy to understand
Line 6: Use -ne instead of -lt. This will ensure your script fails even if number of arguments is more than 3 (not only less than 3)
You don't need to use read (read man page of read) in your code since you are not reading lines from fd. You need the values of your arguments which you are anyway storing in the variables in Line: 2-4
if you use a while loop, make sure the condition fails upon completion so that execution breaks out of the loop. The way to do that is using increment or decrement counters which you are not using in your code
Make sure that you pass absolute path of the directory as the third argument. That way you won't have to use $HOME in your code, when you head or tail
The correct way to use head to show first n lines in head -n [numlines], same goes with tail
IMPORTANT: Also read Charles Duffy comments below, he pointed out really important things, that must not be missed.
find [directory] -type f | while read f; do tail -n [num_lines] "$f"; done
similarly,
find [directory] -type f | while read f; do head -n [num_lines] "$f"; done
My Syst admin prof just started teaching us bash and he wanted us to write a bash script using grep to find all 3-45 letter palindromes in the linux dictionary without using reverse. And im getting an error on my if statement saying im missing a '
UPDATED CODE:
front='\([a-z]\)'
front_s='\([a-z]\)'
numcheck=1
back='\1'
middle='[a-z]'
count=3
while [ $count -ne "45" ]; do
if [[ $(($count % 2)) == 0 ]]
then
front=$front$front_s
back=+"\\$numcheck$back"
grep "^$front$back$" /usr/share/dict/words
count=$((count+1))
else
grep "^$front$middle$back$" /usr/share/dict/words
numcheck=$((numcheck+1))
count=$((count+1))
fi
done
You have four obvious problems here:
First about a misplaced and unescaped backslash:
back="\\$numcheck$back" # and not back="$numcheck\$back"
Second is that you only want to increment numcheck if count is odd.
Third: in the line
front=$front$front
you're doubling the number of patterns in front! hey, that yields an exponential growth, hence the explosion Argument list too long. To fix this: add a variable, say, front_step:
front_step='\([a-z]\)'
front=$front_step
and when you increment front:
front=$front$front_step
With these fixed, you should be good!
The fourth flaw is that grep's back-references may only have one digit: from man grep:
Back References and Subexpressions
The back-reference \n, where n is a single digit, matches the substring
previously matched by the nth parenthesized subexpression of the
regular expression.
In your approach, we'll need up to 22 back-references. That's too much for grep. I doubt there are any such long palindromes, though.
Also, you're grepping the file 43 times… that's a bit too much.
Try this:
#!/bin/bash
for w in `grep -E "^[[:alnum:]]{3,45}$" /usr/share/dict/words`; do if [[ "$w" == "`echo $w|sed "s/\(.\)/\1\n/g"|tac|tr -d '\012'`" ]]; then echo "$w == is a palindrome"; fi; done
OR
#!/bin/bash
front='\([a-z]\)'
numcheck=1
back='\1'
middle='[a-z]'
count=3
while [ $count -ne "45" ]; do
if [[ $(($count % 2)) == 0 ]]
then
front=$front$front
back="\\$numcheck$back"
grep "^$front$back$" /usr/share/dict/words
else
grep "^$front$middle$back$" /usr/share/dict/words
## Thanks to gniourf for catching this.
numcheck=$((numcheck+1))
fi
count=$((count+1))
## Uncomment the following if you want to see one by one and run script using bash -x filename.sh
#echo Press any key to continue: ; read toratora;
done
Permissions links Owner Group Size Date Time Directory or file
-rwxr--r-- 1 User1 root 26 2012-04-12 19:51 MyFile.txt
drwxrwxr-x 3 User2 csstf 4096 2012-03-15 00:12 MyDir
I have problem for pattern match to get certain details using the above details. I actually need to write down the shell script to get the following details.
I need to use pipe in this question. When I do ls -la | prog.sh it need to show the details below.
The major part I don't get is how to use sed pattern matching.
1. Total number of lines read.
2. Total number of different users (owners).
3. Total number of files with execute permission for the owner.
4. The top 3 largest directory.
This is what I have tried so far
#!/bin/bash
while read j
do
B=`sed -n '$=' $1`
echo "total number of lines read = $B"
done
The while loop reads the output of ls -la line by line and you need to process each line and maintain variables for the information you need.
Here is a sample script to get you started:
#!/bin/bash
declare -i lineCount=0
declare -i executePermissionCount=0
# an array to keep track of owners
declare -a owners=()
# read each line into an array called lineFields
while read -r -a lineFields
do
# the owner is the third element in the array
owner="${lineFields[2]}"
# check if we have already seen this owner before
found=false
for i in "${owners[#]}"
do
if [[ $i == $owner ]]
then
found=true
fi
done
# if we haven't seen this owner, add it to the array
if ! $found
then
owners+=( "$owner" )
fi
# check if this file has owner execute permission
permission="${lineFields[0]}"
# the 4th character should be x
if [[ ${permission:3:1} == "x" ]]
then
(( executePermissionCount++ ))
fi
# increment line count
(( lineCount++ ))
done
echo "Number of lines: $lineCount"
echo "Number of different owners: ${#owners[#]}"
echo "Number of files with execute permission: $executePermissionCount"