I have some IDs that follow this unusual incremental stepping pattern, as follows:
eg600100.etc
eg600101.etc
eg600102.etc
...
eg600109.etc
eg600200.etc
...
eg600209.etc
eg600300.etc
...
eg600909.etc
eg601000.etc
eg601001.etc
...
eg601009.etc
eg601100.etc
...
eg601200.etc
...
eg601909.etc
As far as I can tell, it's broken up like this:
60|01-19|00-09
I'm wanting to build a loop that can iterate over each potential ID incrementally up to the end of the range (which is 601909).
How do I break the number up into those 3 segments to manage the unusual stepping for a loop?
I've looked at seq, but I can't figure out how to make it accept the unusual stepping here so that it does not give me numbers between increments that do not exist as potential IDs as above.
#!/bin/bash
for id in $(seq -w 601909)
do
echo "Testing current ID, which is eg$id.etc"
done
Any ideas?
Try using Brace Expansion:
printf %s\\n eg60{01..19}{00..09}.etc >file
Run this as a test, but you can iterate over such an expansion:
for id in eg60{01..19}{00..09}.etc; do echo Testing ID: $id; done
Related
I need to make a program that takes as arguments a number of files that contain lines like this: num1:num2.
I need to store the left column of numbers in one array and the right column and then do some things to them. I need some help on the first part.
The number of files passed as arguments is variable. Also I don't know the name of the files neither how many lines they have. I just know that I will get at least 1 file with 1 line.
I am trying to make a loop for each argument file and then read each file line, break down each line with some string manipulation and then store the results in the 2 arrays. However I haven't succeeded. I know that I also have other kinds of mistakes but I can fix those.
When I try to run the program using:
sh <my_program_name>.sh <argument1_filename>
I just get no results on the terminal, blank screen like it is calculating something in an endless loop.
#!/bin/bash
length=0
b=1
c=1
d=0
args=$#
j=0
temp=0
temp2=0
temp3=0
temp4=0
for temp in "$#"
do
while read line
do
stringtmp=line
tmp=`expr index "$stringtmp" :`
let tmp=tmp-1
stringtmp2='expr substr $stringtmp $1 $tmp'
lengh=`expr index "$stringtmp" \n`
let tmp=tmp+2
let lengh=lengh-1
stringtmp3='expr substr $stringtmp $tmp $lengh'
array1[$length]=stringtmp2
array2[$length]=stringtmp3
let length=length+1
done
...
done
Your while loop is waiting for input from stdin. If you want to loop through contents of temp, you could use:
while read line; do
...
done<$temp
I'm working on getting accustomed to shell scripting and ran across a behavior I found interesting and unexplained. In the following code the first for loop will execute correctly but the second will not.
declare letters=(a b c d e f g)
for i in {0..7}; do
echo ${letters[i]}
done
for i in {0..${#letters[*]}}; do
echo ${letters[i]}
done
The second for loop results in the following error:
syntax error: operand expected (error token is "{0..7}")
What confuses me is that ${#letters[*]} is clearly getting evaluated, correctly, to the number 7. But despite this the code fails even though we just saw that the same loop with {0..7} works perfectly fine.
What is the reason for this?
I am running OS X 10.12.2, GNU bash version 3.2.57.
The bracket expansion happens before parameter expansion (see EXPANSIONS in man bash), therefore it works for literals only. In other words, you can't use brace expansion with variables.
You can use a C-style loop:
for ((i=0; i<${#letters[#]}; i++)) ; do
echo ${letters[i]}
done
or an external command like seq:
for i in $(seq 1 ${#letters[#]}) ; do
echo ${letters[i-1]}
done
But you usually don't need the indices, instead one loops over the elements themselves, see #TomFenech's answer below. He also shows another way of getting the list of indices.
Note that it should be {0..6}, not 7.
Brace expansion occurs before parameter expansion, so you can't use a variable as part of a range.
Expand the array into a list of values:
for letter in "${letters[#]}"; do
echo "$letter"
done
Or, expand the indices of the array into a list:
for i in ${!letters[#]}; do
echo "${letters[i]}"
done
As mentioned in the comments (thanks), these two approaches also accommodate sparse arrays; you can't always assume that an array defines a value for every index between 0 and ${#letters[#]}.
I'm very new to bash scripting, and as I've been searching for information online I've found a lot of seemingly contradictory advice. The thing I'm most confused about is the $ in front of variable names. My main question is, when is and isn't it appropriate to use that syntax? Thanks!
Basically, it is used when referring to the variable, but not when defining it.
When you define a variable you do not use it:
value=233
You have to use them when you call the variable:
echo "$value"
There are some exceptions to this basic rule. For example in math expresions, as etarion comments.
one more question: if I declare an array my_array and iterate through
it with a counter i, would the call to that have to be $my_array[$i]?
See the example:
$ myarray=("one" "two" "three")
$ echo ${myarray[1]} #note that the first index is 0
two
To iterate through it, this code makes it:
for item in "${myarray[#]}"
do
echo $item
done
In our case:
$ for item in "${myarray[#]}"; do echo $item; done
one
two
three
I am no bash user that knows too much. But whenever you declare variable you would not use the $, and whenever you want to call upon that variable and use its value you would use the $ sign.
I want to pass a variable number of 'tuples' as arguments into a bash script and go through them in a loop using pattern matching, something like this:
for *,* in "$#"; do
#do something with first part of tuple
#do something with second part of tuple
done
is this possible? If so, how do I access each part of the tuple?
For example I would like to call my script like:
bash bashscript.sh first_file.xls,1 second_file,2 third_file,2 ... nth_file,1
Since bash doesn't have a tuple datatype (it just has strings), you need would need to encode and decode them yourself. For example:
$ bash bashscript.sh first_file.xls,1 second_file,2 third_file,2 ... nth_file,1
In bashscript.sh:
for tuple in "$#"; do
IFS=, read first second <<< "$tuple"
...
done
Yes, it's possible, and there is more than one way to do it. You can use the prefix/suffix expansion syntax on variables (e.g. ${var#prefix}, ${var##prefix}, ${var%suffix}, ${var%%suffix} - these remove either the shortest or longest prefix/suffix matching the specified pattern). Or you can replace the positional parameters with e.g. IFS=, set -- ${var} (although you'd have to make sure to save the rest of the original parameters in some way first so you can continue your loop). You can use arrays, if your version of bash is new enough (and if it isn't it's pretty old...). Those are probably three of the better methods, but there are others...
Edit: some examples using the suffix/prefix expansions:
for tuple in first_file.xls,1
do
echo ${tuple%,*} # "first_file.xls"
echo ${tuple#*,} # "1"
done
If your tuples are more than 2-ary, that method's a little more complex; for example:
for tuple in x,y,z
do
first=${tuple%%,*}
rest=${tuple#${first}}
second=${rest%%,*}
last=${rest#*,}
done
In that case you might prefer #chepner's answer of IFS=, read first second third <<< "${tuple}"... Otherwise, the bookkeeping can get hairy for large tuples. Setting an array from the tuple would be an acceptable alternative as well.
For simple pairs, though, I tend to prefer just stripping off a prefix/suffix as appropriate...
I am trying to read part of a file and stop and a particular line, using bash. I am not very familiar with bash, but I've been reading the manual and various references, and I don't understand why something like the following does not work (but instead produces a syntax error):
while { read -u 4 line } && (test "$line" != "$header_line")
do
echo in loop, line=$line
done
I think I could write a loop that tests a "done" variable, and then do my real tests inside the loop and set "done" appropriately, but I am curious as to 1) why the above does not work, and 2) is there some small correction that would make it work? I still fairly confused about when to use [, (, {, or ((, so perhaps some other combination would work, though I have tried several.
(Note: The "read -u 4 line" works fine when I call it above the loop. I have opened a file on file descriptor 4.)
I think what you want is more like this:
while read -u 4 line && test "$line" != "$header_line"
do
...
done
Braces (the {} characters) are used to separate variables from other parts of a string when whitespace cannot be used. For example, echo "${var}x" will print the value of the variable var followed by an x, but echo "$varx" will print the value of the variable varx.
Brackets (the [] characters) are used as a shortcut for the test program. [ is another name for test, but when test detects that it was called with [ it required a ] as its last argument. The point is clarity.
Parenthesis (the () characters) are used in a number of different situations. They generally start subshells, although not always (I'm not really certain in case #3 here):
Retrieving a single exit code from a series of processes, or a single output stream from a sequence of commands. For example, (echo "Hi" ; echo "Bye") | sed -e "s/Hi/Hello/" will print two lines, "Hello" and "Bye". It is the easiest way to get multiple echo statements to produce a single stream.
Evaluating commands as if they were variables: $(expr 1 + 1) will act like a variable, but will produce the value 2.
Performing math: $((5 * 4 / 3 + 2 % 1)) will evaluate like a variable, but will compute the result of that mathematical expression.
The && operator is a list operator - he seperates two commands and only executes when the first is true, but in this case the first is the while and he is expecting his do stuff. And then he reaches do and the while stuff is already history.
Your intention is to put it into the expression. So you put it together with (). E.g. this a solution with just a small change
while ( read -u 4 line && test "$line" != "$header_line" )