batch: multiple FOR parameter taken from command arguments - loops

this is what I'm trying to do
find.bat:
#echo off
SET for_argument=%1
SET other_argument2=%2
SET other_argument3=%3
FOR %%A IN (%for_argument%) DO (
echo %%A
rem do other stuff
)
What I want to do is call
find.bat "1 2 3 4" arg2 arg3
and I want that FOR to be executed with 1 2 3 4 as separated arguments, so that the output is
1
2
3
4
But unfortunately with this code the output is
"1 2 3 4"
Can you help me?
Thanks!

SET "for_argument=%~1"
So you get in the for argument a b c d, but without the quotes, this is important for the FOR loop.
A quoted string like "a b c d" is handled as one token, but a b c d is split into four tokens, allowed delims are space "," ";" or "=".

Related

Reading 3 consecutive lines with awk? (Or something else)

I have a huge file, and at some point it goes like this:
Bla bla bla
LAST ITERATION: 1780 6 12 0.689655172413793
-8708.81862246834 -8698.33572943212 -2003.09638506407
-9.912281246897692E-003
Bla bla bla
I would like to get all the numbers after "LAST ITERATION:" and put it in a line in a file.
I have managed to get the two first lines with this:
awk 'a && NR==n{ print a,b,c,d,$1,$3 } /LAST ITERATION:/{ a=$3; b=$4; c=$5; d=$6; n=NR+1 }' ./$FOLDER/$NAMEDATA >> $NAMEOUTPUT
But I can't seem to find a way to get the last number which is on the 3rd line. Could anyone help me?
Thanks!
If you are using gawk, where RS can be more than one character, an easier approach would be to use "LAST ITERATION:" as the record separator, and print the first 8 fields of the second record, with a space as the output record seperator:
gawk 'BEGIN{RS="LAST ITERATION:";ORS=" "}NR==2{for(n=1;n<=8;++n)print$n}'
Demo: https://ideone.com/UzwkdN
You might use getline Variable to access future lines, I would use GNU AWK for this task following way, let file.txt content be
Bla bla bla
LAST ITERATION: 1780 6 12 0.689655172413793
-8708.81862246834 -8698.33572943212 -2003.09638506407
-9.912281246897692E-003
Bla bla bla
then
awk '/LAST ITERATION/{getline x1;getline x2;line=$0 " " x1 " " x2;sub(/LAST ITERATION:[[:space:]]+/,"",line);gsub(/[[:space:]]+/," ",line);print line}' file.txt
gives output
1780 6 12 0.689655172413793 -8708.81862246834 -8698.33572943212 -2003.09638506407 -9.912281246897692E-003
Explanation: when LAST ITERATION is encountered I save next line to variable x1 and next next line to variable x2, then construct line from space-sheared current line, next line and next next line, then remove LAST ITERATION: and following whitespaces characters using sub function and alter multiple whitespace characters to single spaces using gsub, after doing that I print said line. Disclaimer: this solution assumes there are always at least 2 lines after line with LAST ITERATION.
(tested in gawk 4.2.1)
If you are using regular awk, you can concatenate the 3 lines starting with the line with LAST ITERATION: to a variable, and split it in the end:
awk 'BEGIN{ORS=" "}/LAST ITERATION:/{n=NR+3}NR<n{s=s$0}END{split(s,a);for(i=3;i<11;++i)print(a[i])}'
If I read your question correctly, you say you want to see an entry within a file, followed by two following lines.
Next to that, you seem to want to work with awk.
My answer is: "Why awk? This can very easily be done using grep.", as follows:
grep has three switches to add more lines, next to the matching one:
-A n : add "n" lines after the match
-B n : add "n" lines before the match
-C n : add "n" lines, combined after and before
So, you can simply use grep -A 2 "match" filename.
Good luck
For batch you can target lines using findstr:
The below can target single lines, consecutive lines or a list of lines.
#Echo off
REM :: CALL %0 <filepath> <integer|integer list>
Set "File.Name="
If exist "%~f1" (
Set "File.Name=%~f1"
)Else (
1>&2 Echo(File Not Found
Exit /B 1
)
If "%~2" == "" (
1>&2 Echo(Missing Arg/s for target Line/s.
Exit /B 2
)
Set Target.Lines=%*
Set "Target.Lines=%Target.Lines:"=%"
For /f "Delims=" %%G in ('CMD /V:ON /C "Echo(!Target.Lines:%~1 =!"')Do Set "Target.Lines=%%G"
Set "Line.Count=0"
Set "Highest.Target=0"
Setlocal EnableExtensions EnableDelayedExpansion
For %%G in (%Target.Lines%)Do if !Highest.Target! LSS %%G Set "Highest.Target=%%G"
Endlocal & Set "Highest.Target=%Highest.Target%"
For /f "Tokens=1* Delims=:" %%G in ('%SystemRoot%\System32\findstr.exe /VNLC:"{false.string[%~n0]}" "%File.Name%"')Do (
Set "Line.Count=%%G"
For %%i in (%Target.Lines%)Do if %%G EQU %%i Echo(%%H
)
If %Line.Count% LSS %Highest.Target% (
1>&2 Echo(Lines missing. Lines in file: %Line.Count%. Highest Target: %Highest.Target%
)
awk '
gsub(/LAST ITERATION:/,""){
gsub(/^ */,"");
o=sprintf("%s ",$0);
while(getline && $1 ~ /^[-]?[0-9]+(\.[0-9]*)?/) {
o=o sprintf("%s ", $0)
}
print gensub(/ */," ", "g", o) > "output_file"
}
' input_file
$ cat output_file
1780 6 12 0.689655172413793 -8708.81862246834 -8698.33572943212 -2003.09638506407 -9.912281246897692E-003

Surprising array expansion behaviour

I've been surprised with the line marked (!!) in the following example:
log1 () { echo $#; }
log2 () { echo "$#"; }
X=(a b)
IFS='|'
echo ${X[#]} # prints a b
echo "${X[#]}" # prints a b
echo ${X[*]} # prints a b
echo "${X[*]}" # prints a|b
echo "---"
log1 ${X[#]} # prints a b
log1 "${X[#]}" # prints a b
log1 ${X[*]} # prints a b
log1 "${X[*]}" # prints a b (!!)
echo "---"
log2 ${X[#]} # prints a b
log2 "${X[#]}" # prints a b
log2 ${X[*]} # prints a b
log2 "${X[*]}" # prints a|b
Here is my understanding of the behavior:
${X[*]} and ${X[#]} both expand to a b
"${X[*]}" expands to "a|b"
"${X[#]}" expands to "a" "b"
$* and $# have the same behavior as ${X[*]} and ${X[#]}, except for their content being the parameters of the program or function
This seems to be confirmed by the bash manual.
In the line log1 "${X[*]}", I therefore expect the quoted expression to expand to "a|b", then to be passed to the log1 function. The function has a single string parameter which it displays. Why does something else happen?
It'd be cool if your answers were backed by manual/standard references!
IFS is used not just to join the elements of ${X[*]}, but also to split the unquoted expansion $#. For log1 "${X[*]}", the following happens:
"${X[*]}" expands to a|b as expected, so $1 is set to a|b inside log1.
When $# (unquoted) is expanded, the resulting string is a|b.
The unquoted expansion undergoes word-splitting with | as the delimiter (due to the global value of IFS), so that echo receives two arguments, a and b.
That's because $IFS is set to |:
(X='a|b' ; IFS='|' ; echo $X)
Output:
a b
man bash says:
IFS The Internal Field Separator that is used for word splitting after expansion ...
In the POSIX spec section on [Special Parameters[(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02) we find.
#
Expands to the positional parameters, starting from one. When the expansion occurs within double-quotes, and where field splitting (see Field Splitting) is performed, each positional parameter shall expand as a separate field, with the provision that the expansion of the first parameter shall still be joined with the beginning part of the original word (assuming that the expanded parameter was embedded within a word), and the expansion of the last parameter shall still be joined with the last part of the original word. If there are no positional parameters, the expansion of '#' shall generate zero fields, even when '#' is double-quoted.
*
Expands to the positional parameters, starting from one. When the expansion occurs within a double-quoted string (see Double-Quotes), it shall expand to a single field with the value of each parameter separated by the first character of the IFS variable, or by a if IFS is unset. If IFS is set to a null string, this is not equivalent to unsetting it; its first character does not exist, so the parameter values are concatenated.
So starting with the quoted variants (they are simpler):
We see that the * expansion "expand[s] to a single field with the value of each parameter separated by the first character of the IFS variable". This is why you get a|b from echo "${X[*]" and log2 "${X[*]}".
We also see that the # expansion expands such that "each positional parameter shall expand as a separate field". This is why you get a b from echo "${X[#]}" and log2 "${X[#]}".
Did you see that note about field splitting in the spec text? "where field splitting (see Field Splitting) is performed"? That's the key to the mystery here.
Outside of quotes the behavior of the expansions is the same. The difference is what happens after that. Specifically, field/word splitting.
The simplest way to show the problem is to run your code with set -x enabled.
Which gets you this:
+ X=(a b)
+ IFS='|'
+ echo a b
a b
+ echo a b
a b
+ echo a b
a b
+ echo 'a|b'
a|b
+ echo ---
---
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 'a|b'
+ echo a b
a b
+ echo ---
---
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 'a|b'
+ echo 'a|b'
a|b
The thing to notice here is that by the time log1 is called in all but the final case the | is already gone.
The reason it is already gone is because without quotes the results of the variable expansion (in this case the * expansion) are field/word split. And since IFS is used both to combine the fields being expanded and then to split them again the | gets swallowed by field splitting.
And to finish the explanation (for the case actually in question), the reason this fails for log1 even with the quoted version of the expansion in the call (i.e. log1 "${X[*]}" which expands to log1 "a|b" correctly) is because log1 itself does not use a quoted expansion of # so the expansion of # in the function is itself word-split (as can be seen by echo a b in that log1 case as well as all the other log1 cases).

How to expand the elements of an array in zsh?

Say I have an array in zsh
a=(1 2 3)
I want to append .txt to each element
echo ${a}.txt # this doesn't work
So the output is
1.txt 2.txt 3.txt
UPDATE:
I guess I can do this, but I think there's a more idiomatic way:
for i in $a; do
echo $i.txt
done
You need to set RC_EXPAND_PARAM option:
$ setopt RC_EXPAND_PARAM
$ echo ${a}.txt
1.txt 2.txt 3.txt
From zsh manual:
RC_EXPAND_PARAM (-P)
Array expansions of the form `foo${xx}bar', where the parameter xx is set to
(a b c), are substituted with `fooabar foobbar foocbar' instead of the
default `fooa b cbar'. Note that an empty array will therefore cause all
arguments to be removed.
You can also set this option just for for one array expansion using ^ flag:
$ echo ${^a}.txt
1.txt 2.txt 3.txt
$ echo ${^^a}.txt
1 2 3.txt
Again citing zsh manual:
${^spec}
Turn on the RC_EXPAND_PARAM option for the evaluation of spec; if the `^' is
doubled, turn it off. When this option is set, array expansions of the form
foo${xx}bar, where the parameter xx is set to (a b c), are substituted with
`fooabar foobbar foocbar' instead of the default `fooa b cbar'. Note that an
empty array will therefore cause all arguments to be removed.

Bash Array - Cant get input from a loop in to my array correctly

I run this script as
script.sh 'abcdefghijk123456789!##$%^&' 'aaaa4444ged###'
it should be able to produce an array containing something like
1 1 1 1 15 15 15 15 7 5 23 23 23
with 12 indexes 0 - 11, but what i get is
1 1 1 1 1 5 1 5 1 5 1 5 7 5 2 3 2 3 2 3
with 20 indexes.
I want to populate a array with base numbers for a given char.
So i have a list of chars that we will be using in $startstring ie.
abcc5678% we can say that every char in $startstring is = to one char in the
$charset ie. abcd5678!%. This code finds what each $startstring char is equal
to $charset's index number. That information is what i am trying to capture in a
array. Mostly it works except a bug where instead of the whole number 10 getting
stored in decoded[1] what happens is the number 10 is split in to 1 and 0 and
then both are put in under separate indexes. So instead of a "1 10 1" and 3
indexes i end up with 4 indexes with "1 1 0 1". Im sure im just handling my
variables the wrong way but i searched and searched and now my brain is gonna
explode so i came here for some relief. or hope of it anyway. Can someone tell
me the proper way to insert digits in to this decoded[] array?
#!/bin/bash
#declare -i decoded
charset=$1
startstring=$2
start=$((${#charset}-1))
echo "Setting up CharMap to CharSet"
for i in $(eval echo {0..$start})
do
echo $i " = " ${charset:$i:1}
done
echo "Proving Chars Were Mapped Correctly."
start2=$((${#startstring}))
start3=$((${#charset}-1))
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo "found that" ${startstring:$i:1}"=" $p 'from the charmap.'
decoded+=$p #<--### I DONT THINK THIS IS WHAT I NEED ###
fi
done
done
##################Just trying to print my new array#########################
start4=$((${#decoded}-1))
echo 'Testing the array $decoded'
echo 'the var start4(length of $decoded) = '$start4
echo 'this number should equal ----------> '$start2
echo 'Printing out the $decoded array in a for loop'
for c in $(eval echo {0..$start4})
do
echo ${decoded[$c]} ###DOESNT WORK LIKE I THOUGHT# also tried echo ${decode:$c:1}
done
decoded+=$p appends $p as a string, not as an array entry. Essentially, you're creating the string "11111515151575232323" by appending all the index numbers together. (Actually, I get "00001414141464322222227" from your example, because of index bound problems. I'll let you worry about that...)
To store the decoded values as an array, set decoded to an empty array before the loop, and then use decoded+=("$p") to add $p as an element:
decoded=() # create an empty array
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo ${startstring:$i:1} "=" $p
decoded+=("$p") # append $p as a new array element
fi
done
done
Then, to get the size of the array (rather than a string length), use ${#decoded[#]}:
start4=$((${#decoded[#]}-1))

Comparing two files

Can anyone create a script for a file comparison?
The pseudo code looks like this
extract line 5 from file 1
save it in another variable,
extract line 5 from file 2,
save it in another variable,
if
variable 1 is not equal to variable 2 (while allowing for differing characters at positions 11 and 12 from the end of line)
then fail
else
pass
Unix shell? Pass the fileneames as arguments to the following script:
#!/bin/sh
a="$(head -n 5 < $1|tail -n 1)"
b="$(head -n 5 < $2|tail -n 1)"
test "$a" = "$b" && (echo The same; exit 0)
echo Different; exit 1

Resources