Here in the code below, line is a line of strings returned as the output of a command.
When I run the script, it gives me all the tokens of the string, but appends a listing of the directory to it as well. I really can't figure out what I am doing wrong.
for word in $line
do
inner_count=$((inner_count + 1))
echo $word
done
Here is the entire piece of code:
while read -r line
do
if [ "$count" = "2" ];
then
inner_count=0
#parse each line
#if [ "$debug" = "1" ] ; then printf "%s\n" "$line" > /dev/kmsg ; fi
for word in $line
do
if [ "$inner_count" = "0" ]; then tmp1="$word" ; fi
if [ "$inner_count" = "4" ]; then temp2="$word" ;fi
inner_count=$((inner_count + 1))
done
fi
count=$((count + 1))
done < <(batctl tg)
The most likely issue that I can think of that could produce this would be that there is a * in $line, and the shell is expanding that (globbing). You can disable globbing with set -f.
Try:
set -f # disable globbing
for word in $line
do
inner_count=$((inner_count + 1))
echo "$word"
done
set +f # re-enable globbing
Related
This script is incomplete since I will want to do error testing later, but the idea is that ARG is a script and ARG2 is a directory, and ARG should mark all of the files in ARG2. How would I do this in such a way that bash knows the first argument has to be a script and argument 2 is a directory?
ARG=$1
ARG2=$2
CHECK=0
aCount=0
bCount=0
cCount=0
dCount=0
fCount=0
if [ $CHECK -e 0 ]; then
for files in $ARG2; do
if [ sh $1 $2 -eq A]; then
aCount=$((aCount+1))
elif [ sh $1 $2 -eq B];
bCount=$((bCount+1))
elif [ sh $1 $2 -eq C];
cCount=$((cCount+1))
elif [ sh $1 $2 -eq D ];
dCount=$((dCount+1))
else;
fCount=$((fCount+1))
fi
done
fi
echo A: $aCount
echo B: $bCount
echo C: $cCount
echo D: $dCount
echo F: $fCount
There are a variety of errors you could catch by running your script through shellcheck.net.
Corrections:
To loop over the files in a directory, write for file in dir/* not for file in dir. The latter just loops once with $file set to the string "dir", rather than iterating over the contents of the directory dir/.
[ sh $1 $2 -eq A] is a jumble of shell constructs. You want to capture the output of the script, so you need $(...). You're doing a string check, so you should use == not -eq. Correcting both yields:
[ $(sh $1 $2) == A ]
I'm guessing $2 should be $files, though. The loop variable, yes?
[ $(sh $1 $files) == A ]
There are other miscellaneous mistakes, such as missing thens and not always having a space before ].
Improvements:
You should quote everything properly to prevent inadvertent word splitting and glob expansion.
[ "$(sh "$1" "$files")" == A ]
Let's replace $1 with $script and $files with singular $file.
[ "$(sh "$script" "$file")" == A ]
If the script has a proper shebang line like #!/bin/bash at the top then there's no need to explicitly invoke sh.
[ "$("$script" "$file")" == A ]
That's all great. Now you have something like this:
if [ "$("$script" "$file")" == A ]; then
aCount=$((aCount+1))
elif [ "$("$script" "$file")" == B ]; then
bCount=$((bCount+1))
elif [ "$("$script" "$file")" == C ]; then
cCount=$((cCount+1))
elif [ "$("$script" "$file")" == D ]; then
dCount=$((dCount+1))
else
fCount=$((fCount+1))
fi
Awfully repetitious, no? Let's try a case statement instead.
case "$("$script" "$file")" in
A) aCount=$((aCount+1));;
B) bCount=$((bCount+1));;
C) cCount=$((cCount+1));;
D) dCount=$((dCount+1));;
*) fCount=$((fCount+1));;
esac
That case statement is still quite complex. Let's break it up to make it easier to parse.
grade=$("$script" "$file")
case $grade in
...
esac
Variable names ought to be lowercase. UPPERCASE names are reserved for the shell, so best not to use those. Change COUNT to count.
Let's rename ARG and ARG2 to script and dir, respectively. Meaningful names make everything easier to read.
var=$((var+1)) can be simplified to ((var += 1)) or ((var++)).
End result:
script=$1
dir=$2
check=0
aCount=0
bCount=0
cCount=0
dCount=0
fCount=0
if ((check == 0)); then
for file in "$dir"/*; do
grade=$("$script" "$file")
case $grade in
A) ((aCount++));;
B) ((bCount++));;
C) ((cCount++));;
D) ((dCount++));;
*) ((fCount++));;
esac
done
fi
echo "A: $aCount"
echo "B: $bCount"
echo "C: $cCount"
echo "D: $dCount"
echo "F: $fCount"
#John Kugelman did a great job above. For an alternate take -
declare -A count # count is an array
for file in "$dir"/* # skipping assignments, and $check
do grade=$("$script" "$file") # grab the output as $grade
case $grade in # look up its value
[A-D]) (( count[$grade]++ ));; # use as-is for a-d
*) (( count['F']++ ));; # throw everything else in f
esac
done
for g in A B C D F # then for a-f (known values)
do echo "$g: "${count[$g]} # pull the counts
done
Below is my shell script code:
#!/bin/bash
first=0
my_for_loop()
{
first="$1";
if [ $first -eq 0 ]
then
return 1;
elif [ $first -eq 1 ]
then
return 0;
fi
}
while read line; do
for word in $line; do
echo "word = '$word'"; ( echo "$word" | grep -Eq "\([0-9]+\)" ) && ( word="${word%\)}"; word="`echo ${word} | cut -d'(' -f 2`"; echo "no = $word";
my_for_loop "$first"; first="$?"; echo "foo = $first")
done
echo "first value is $first" )
done
Basically I'm trying to read the words from the input and checking whether it is of the form "([0-9]+)" {e.g. (120) } if so, extracting the numerals i.e 120
The problem arises when I'm trying to set the variable "first" to 1 on the odd no of occurence of the pattern, and to 0 on eventh occurence
I tried Process Substitution, but not working porperly.
Can you help me with setting the boolean value inside the for loop such that it retains the value during next iteration also?
Avoid subprocesses. Is this want you want:
i=0
while read line ; do
cutted=$(sed 's/^(\([0-9]*\))$/\1/' <<< $line)
if [ "${line}" != "${cutted}" ]; then
(( i = !i))
echo "First=$i and ${cutted}"
fi
done
and use another loop when you cut things in words
i=0
while read line ; do
for word in $line; do
echo "${word}"
cutted=$(sed 's/^(\([0-9]*\))$/\1/' <<< ${word})
if [ "${word}" != "${cutted}" ]; then
(( i = !i))
echo "First=$i and ${cutted}"
fi
done
done
I have done this way to read text file by using help from these : Split string into an array in Bash and How do I split a string on a delimiter in Bash? and trying to split string but i am getting only 1st line with this code: (i have pasted the same code in "file" which is i am reading.)
#!/bin/bash
i=0
for file in $(ls -l)
do
if [ -f $file ] #checking for regular file
then
if [ $file == Q1 ] || [ $file == Q1~ ]
then
continue
else
if [ -f $file ]
then
echo $file
v=$(<$file)
# echo ${v[0]} # prints all
#------------------ split string-----------------------
OIFS="$IFS"
IFS='' # split by spaces
read -a array <<< "${v}"
IFS="$OIFS"
echo ${array[0]} #
#printf "\n"
# sed -i 's/$1/$2/g' "$file"
else
echo "Error: Not found"
fi
#i=$i+1
#echo $i
fi
fi
done
exit 0
but when using with loops, then there is no splitting (2nd code)
#!/bin/bash
i=0
for file in $(ls -l)
do
if [ -f $file ] #checking for regular file
then
if [ $file == Q1 ] || [ $file == Q1~ ]
then
continue
else
if [ -f $file ]
then
echo $file
v=$(<$file)
# echo ${v[0]} # prints all
#------------------ split string-----------------------
OIFS="$IFS"
while IFS='' read -a array;
do
for i in "${array[#]}";do
echo ${array[1]} #
done
done <<< "$v"
#printf "\n"
# sed -i 's/$1/$2/g' "$file"
else
echo "Error: Not found"
fi
#i=$i+1
#echo $i
fi
fi
done
exit 0
A beginner asking for help (:
So, I have a script that checks brackets in a text file and tells whether they are closed correctly. However, I also want to make my script print out the number of the incorrect line (where brackets are closed incorrectly). I have tried counting file lines and then making a nested while loop, however, it doesn't work for me at all ): Are there any simple solutions for this? I would like to leave the LINE counter if that's possible o:
INPUT="$1"
count=0
LINE=0
# Check if file exists
[ ! -f $INPUT ] && { echo "file $INPUT do not exist."; exit ; }
# Count file lines and read every char
while IFS= read -r LINE
do
LINE=$(( LINE + 1 ))
while read -n1 char
do
[ "$char" == "(" ] && (( count++ ))
[ "$char" == ")" ] && (( count-- ))
if [ "$count" -lt 0 ]
then
break
fi
done
done < "$INPUT"
if [ "$count" -lt 0 ]
then
echo "Found a mistake in $LINE line "
else
echo "Everything's correct"
fi
You have a couple of problems:
Your read in the inner loop consumes the input from the file, not from LINE.
The line
LINE=$(( LINE + 1 ))
is really wrong: LINE is the content of the line of your file, and your trying to add 1 to it. Weird.
Your break only breaks the inner loop (it should break two loops). Use break 2 for this.
Here's a working version of your script:
input=$1
count=0
linenb=0
# Check if file exists
[[ -f $input ]] || { echo "Error: file $input do not exist."; exit 1; }
# Count file lines and read every char
while IFS= read -r line; do
((++linenb))
while read -n1 char; do
[[ $char == '(' ]] && ((++count))
[[ $char == ')' ]] && ((--count))
((count>=0)) || break 2
done <<< "$line"
done < "$input"
if ((count<0)); then
echo "Found a mistake in line #$linenb:"
printf '%s\n' "$line"
else
echo "Everything's correct"
fi
Note that I used more ((...)) and [[...]].
I also used lowercase variable names, as your computer isn't deaf: you don't need to shout the name of the variable. (And it's nicer to the eye). And it's good practice to use lowercase variable names, as there's no chance that they clash with Bash's own variables.
Aside from running every code path that has an NSLocalizedString in it, is there a way to verify that all NSLocalizedStrings have a key that actually exists in all your Localizable.strings files of all your bundles?
E.g. there wasn't a typo in one key such that NSLocalizedString won't find the key it's looking for?
OK I wrote a bash script to accomplish the above. Here it is. It took me hours so don't forget to up-vote me if you like. Feel free to make improvements, etc. I added a few comments suggesting potential improvements.
#!/bin/sh
# VerNSLocalizedStrings
while getopts "vsl:" arg; do
case $arg in
v)
verbose="yes"
;;
s)
stopOnMissing="yes"
;;
l)
lang=$OPTARG
;;
esac
done
if [[ -z $lang ]]
then
lang="en"
fi
searchDir=$lang.lproj
fileFound=`ls . | grep $searchDir`
if [[ -z $fileFound ]]
then
echo "dir "$searchDir" not found."
exit
fi
fileFound=`ls $searchDir/ | grep strings`
if [[ -z $fileFound ]]
then
echo "No .strings files found in dir "$searchDir"."
exit
fi
echo "Verifying NSLocalizationStrings in "$searchDir
# Get all the NSLocalizedString Commands
output=$(grep -R NSLocalizedString . --include="*.m")
# Go thru the NSLocalizedString commands line for line
count=$(( 0 ))
missing=$(( 0 ))
unusable=$(( 0 ))
OIFS="${IFS}"
NIFS=$'\n'
IFS="${NIFS}"
for LINE in ${output} ; do
IFS="${OIFS}"
# Now extract the key from it
# admittedly this only works if there are no line breaks between
# NSLocalizedStrings and the entire key,
# but it accounts for the keys it couldn't identify.
quotes=`echo $LINE | awk -F\" '{ for(i=2; i<=NF; i=i+2){ a = a"\""$i"\"""^";} {print a; a="";}}'`
key=`echo $quotes | cut -f1 -d"^"`
# If we couldn't find the key then flag problem
if [[ -z $key ]]
then
(( unusable += 1 ))
echo "Couldn't extract key: " $LINE
if [ -n "$stopOnMissing" ]
then
break
else
continue
fi
fi
# I don't know how grep works regarding length of string, only that
# if the string is too long then it doesn't find it in the file
keyLength=$(echo ${#key})
if [ $keyLength -gt 79 ]
then
(( unusable += 1 ))
echo "Key too long ("$keyLength"): " $key
if [ -n "$stopOnMissing" ]
then
break
else
continue
fi
fi
# It would be nice if this were a regular expression that allowed as many
# spaces as you want, even a line break then forced the quotes on the
# other side of the equal sign.
keyString=$key" ="
# Search for the key
found=$(iconv -sc -f utf-16 -t utf8 $searchDir/*.strings | grep "$keyString")
# damned if I know why some strings files are utf-16 and others are utf8
if [[ -z $found ]]
then
found=$(grep -r "$keyString" $searchDir/ --include=*.strings)
fi
# analyze the result
if [[ -z $found ]]
then
(( missing += 1 ))
echo "Missing: " $key "\n from: " $LINE
if [ -n "$stopOnMissing" ]
then
break
fi
else
if [ -n "$verbose" ]
then
echo "found: " $key
fi
fi
(( count += 1 ))
IFS="${NIFS}"
done
IFS="${OIFS}"
# It would also be nice if it went the other way and identified
# extraneous unused items in the strings files. But
# I've spent enough time on this for now
echo $count " keys analyzed"
echo $unusable " keys could not be determined"
echo $missing " keys missing"
To verify that all NSLocalizedStrings have a key that actually exists in all your Localizable.strings files or you missed localised you can enable the Localization enable "Show non-localized strings" option in the your project scheme editor.
Now run application you will see console logs for the missing localised string.