I have following strings in input.txt file.
Running
isn't running
is running
Stopped
stopped
Aborted
aborted
Here I would need to match everything except "Running" and "is running". So far I have done below but it seems to be printing "Running" and "is running" as well. Can some help ?
exec < input.txt
while read line
do
if [[ $line =~ [Aa]borted || [Ss]topped || isn*t ]]; then
echo "$line"
else
echo "FINE"
fi
done
Why don't use grep?
grep -v "unning" < input.txt
gives you desired lines.
grep -vE '\<(R|is r)unning\>' input.txt
The \< and \> markers are word boundaries, so you won't filter out "this running"
In shell
while IFS= read -r line; do
case $line in
*"Running"* | *"is running"*) echo FINE ;;
*) echo "$line" ;;
esac
done < input.txt
Problem in your code: this line
[[ $line =~ [Aa]borted || [Ss]topped || isn*t ]]
is not equivalent to this
[[ $line =~ [Aa]borted ]] || [[ $line =~ [Ss]topped ]] || [[ $line =~ isn*t ]]
it is equivalent to this
[[ $line =~ [Aa]borted ]] || [[ -n "[Ss]topped" ]] || [[ -n "isn*t" ]]
because the "or" operator || has higher precedence than the regex match operator =~
Since the last 2 conditions are always true, each line passes.
Note also that you're using glob patterns with a regular expression match operator, so this [[ $line =~ isn*t ]] will not match isn't, it will match is and t with zero or more n in between. (ist, isnt, isnnt, etc)
You intended to write this
[[ $line == [Aa]borted || $line == [Ss]topped || $line == isn*t ]]
which is mote concisely written with case:
case $line in
[Aa]borted | [Ss]topped | isn*t) echo "$line" ;;
*) echo FINE ;;
esac
which is the inverse of my answer.
Related
I'm trying to automate a build process by replacing .js chunks for particular lines in my main.config.php file. When I run the following code:
declare -a js_strings=("footer." "footerJQuery." "headerCSS." "headerJQuery.")
build_path="./build/build"
config_path="./system/Config/main.config.php"
while read -r line;
do
for js_string in ${js_strings[#]}
do
if [[ $line == *$js_string* ]]
then
for js_file in "$build_path"/*
do
result="${js_file//[^.]}"
if [[ $js_file == *$js_string* ]] && [[ ${#result} -eq 3 ]]
then
sed -i "s/$line/$line$(basename $js_file)\";/g" $config_path
fi
done
fi
done
done < "$config_path"
I get this message back, and file has not been updated/edited:
sed: 1: "./system/Config/main.co ...": invalid command code .
I haven't been able to find anything in my searches that pertain to this specific message. Does anyone know what I need to change/try to get the specific lines replaced in my .php file?
Updated script with same message:
declare -a js_strings=("footer." "footerJQuery." "headerCSS." "headerJQuery.")
build_path="./build/build"
config_path="./system/Config/main.config.php"
while read -r line;
do
for js_string in ${js_strings[#]}
do
if [[ $line == *$js_string* ]]
then
for js_file in "$build_path"/*
do
result="${js_file//[^.]}"
if [[ $js_file == *$js_string* ]] && [[ ${#result} -eq 3 ]]
then
filename=$(basename $js_file)
newline="${line//$js_string*/$filename\";}"
echo $line
echo $newline
sed -i "s\\$line\\$newline\\g" $config_path
echo ""
fi
done
fi
done
done < "$config_path"
Example $line:
$config['public_build_header_css_url'] = "http://localhost:8080/build/headerCSS.js";
Example $newline:
$config['public_build_header_css_url'] = "http://localhost:8080/build/headerCSS.7529a73071877d127676.js";
Updated script with changes suggested by #Vercingatorix:
declare -a js_strings=("footer." "footerJQuery." "headerCSS." "headerJQuery.")
build_path="./build/build"
config_path="./system/Config/main.config.php"
while read -r line;
do
for js_string in ${js_strings[#]}
do
if [[ $line == *$js_string* ]]
then
for js_file in "$build_path"/*
do
result="${js_file//[^.]}"
if [[ $js_file == *$js_string* ]] && [[ ${#result} -eq 3 ]]
then
filename=$(basename $js_file)
newline="${line//$js_string*/$filename\";}"
echo $line
echo $newline
linenum=$(grep -n "^${line}\$" ${config_path} | cut -d':' -f 1 )
echo $linenum
[[ -n "${linenum}" ]] && sed -i "${linenum}a\\
${newline}
;${linenum}d" ${config_path}
echo ""
fi
done
fi
done
done < "$config_path"
Using sed's s command to replace a line of that complexity is a losing proposition, because whatever delimiter you choose may appear in the line and mess things up. If these are in fact entire lines, it is better to delete them and insert a new one:
linenum=$(fgrep -nx -f "${line}" "${config_path}" | awk -F : "{print \$1}" )
[[ -n "${linenum}" ]] && sed -i "" "${linenum}a\\
${newline}
;${linenum}d" "${config_path}"
What this does is search for the line number of the line that matches $line in its entirety, then extracts the line number portion. fgrep is necessary otherwise the symbols in your file are interpreted as regular expressions. If there was a match, then it runs sed, appending the new line (a) and deleting the old one (d).
Currently I have
if [[ $INPUT_FILE == *".aac" ]] || [[ $INPUT_FILE == *".aiff" ]] || [[ $INPUT_FILE == *".pcm" ]]
I have tried the following, which does not work. It does not match anything.
if [[ $INPUT_FILE == *".aac" || *".aiff" || *".pcm" ]]
Is there any way to factor this expression?
With [[ you could use a regex (specifically, an Extended Regular Expression)
if [[ $input_file =~ \.(aac|aiff|pcm)$ ]]; then
: something
fi
or an extglob
if [[ $input_file = *.#(aac|aiff|pcm) ]]; then
: something
fi
Or you may use a case statement
case $input_file in
*.aac | *.aiff | *.pcm )
# anything
;;
esac
I am working on a project in Bash that takes a live xlsx file, converts it into a csv file, and checks the file to make sure that the data inside it are urls. This is part of a larger progragam that will eventually test each url for domain squatting.
I am having problems with the verification of the string data. I am having to teach myself bash as i go along since this is a self study class. Thanks for the Help!
INPUT=domain3.csv
while IFS= read -r line
do
if [[ "$line" == *".com"*] || [ "$line" == *".net"*] || [ "$line" == *".org"*] || [ "$line" == *".biz"*]];
then echo "$line"
else echo "$line is not an URL"
fi
echo "Finished!"
done
Use the =~ to perform regular expression match:
if [[ $INPUT =~ \.(com|net|org)$ ]]
then
echo $INPUT is a domain
else
echo $INPUT is not a domain
fi
The expression reads that if $INPUT matches a dot (\.), then one of "com", "net", or "org", then end of string ($), then it is a domain.
[[ ... ]] (since bash 4.1) temporarily enables the extglob option, so you can write
if [[ "$line" == *.#(com|net|org|biz)* ]];
You probably don't actually want the trailing *, which would let you match things like foo.comzzz.
A case statement.
#!/bin/sh
while IFS= read -r line; do
case $line in
*.com|*.net|*.org|*.biz)
echo "$line";;
*) printf >&2 '%s is not a url!\n' "$line" ;;
esac
done
Please execute the below code once and then compare it with your's to find out the error.
while IFS= read -r line
do
if [[ "$line" == *".com"* ]] || [[ "$line" == *".net"* ]] || [[ "$line" == *".org"* ]] || [[ "$line" == *".biz"* ]]
then
echo "$line"
else
echo "$line is not an URL"
fi
done < $INPUT
echo "Finished!"
I'm looking for help with my shell... Hope I'll find it here...
Here's my code :
#!/bin/sh
while IFS= read -r line || [ -n "$line" ]
do
[[ "$line" =~ ^[[:space:]]*\# ]] && continue # This line must stay
[[ "$line" =~ *read[[:space:]]-r[[:space:]]line* ]] && continue
echo "${line%$NL}"
done < $0
First test will suppress "only comment lines".
Second test purpose is to suppress the "while IFS= read..." lines - no matter what, it's just a test :-)
"done < $0" has been here written intentionaly... for the test !
Running the shell outputs this :
while IFS= read -r line || [ -n "$line" ]
do
[[ "$line" =~ ^[[:space:]]*\# ]] && continue # This line must stay
[[ "$line" =~ *read[[:space:]]-r[[:space:]]line* ]] && continue
echo "${line%$NL}"
done < $0
as I thought the first line will be gone because of matching then 2nd test.
What's my mistake ?
For the record, I don't want to use extra sed or awk sentence.
Actually, the input data (here $0 file) has to be standard input (eg extract from tee command). I read lot of stackOverflow subject about this, with sed or awk responses that didn't match my purpose.
The regex is invalid. A short test shows:
> [[ 'while IFS= read -r line || [ -n "$line" ]' =~ *read[[:space:]]-r[[:space:]]line* ]]
> echo $?
2
The * can't be "alone" - it can't be the first character in a POSIX extended regular expression. It has to "bind" to something, ex. a dot .. A dot represents any character. You want:
[[ $line =~ .*read[[:space:]]-r[[:space:]]line.* ]]
Problem is presence of starting quantifier * in the regex in second continue line. You may use:
[[ "$line" =~ read[[:space:]]+-r[[:space:]]+line ]] && continue
There is no need to match anything before read or after line in this regex.
Also it is better to use quantifier + after [[:space:]] to make it match 1 or more white spaces.
You can do more refactoring and combine both regex into one by using alternation as in this code:
while IFS= read -r line || [[ -n $line ]]
do
[[ $line =~ ^[[:space:]]*#|read[[:space:]]+-r[[:space:]]+line ]] && continue
echo "${line%$NL}"
done < $0
I'm stuck in something in my bash script, I have a string that composes of a repetitive sequence of 20s, e.g. 202020, there might be more or less 20s, e.g. 2020 or 2020202020, I want to create an if condition that if finds any strange number inside, e.g. 30 in 20203020, gives an error.
Any ideas?
this should do the test:
[[ "$var" =~ "^(20)+$" ]]
check this:
kent$ [[ "202" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20203" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20202002" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20202020" =~ "^(20)+$" ]] && echo "y" || echo "n"
y
how about this example, can you use it?
if [ "`echo "202020302020" | sed -e 's/20//g'`" ];
then echo "there is something";
fi
Extended patterns are a tiny bit more compact than regular expressions.
shopt -s extglob
if [[ $str = +(20) ]]; then
echo "twenties"
else
echo "mismatch"
fi
At some point, bash changed to treat all patterns in [[...]] expressions as extended patterns, so the shopt command may not be necessary.