Iterate over letters in a for loop - bash

Is it possible to iterate over a list of letters, as followed:
aaaa, aaab, ..., aaaz, aaba, aabb, ..., aabz, ..., zzzy, zzzz
I know the syntax to iterate over the alphabet:
for i in {a..z}
but couldn't figure out a way to do the extended version...
Thanks in advance

You could use brace expansion:
echo {a..z}{a..z}{a..z}{a..z}
Use it in a loop:
for i in {a..z}{a..z}{a..z}{a..z}; do
echo $i
done
It would produce:
aaaa
aaab
aaac
aaad
aaae
...
zzzv
zzzw
zzzx
zzzy
zzzz
You can read more about combining and nesting brace expansions here.

Yes, BASH doesn't care what is contained in the sequence, as long as you give it the sequence.
for i in ducks geese swans; do echo $i; done
ducks
geese
swans
for building further with brace expansion, you just need to work on your brace statements:
for i in aaa{a..z}; do echo $i; done
aaaa
aaab
aaac
aaad
aaae
aaaf
aaag
aaah
aaai
aaaj
aaak
...
Take a look at brace expansion in man bash. You can use the above to satisfy your needs by a set of nested loops with differing levels of prefix for your expansion setups.

I actually don't know bash well, but wouldn't that work?
// Pseudocode
for i in {a..z}
for j in {a..z}
for k in {a..z}
for l in {a..z}
echo $i$j$k$l

Related

Looping through variable with spaces

This piece of code works as expected:
for var in a 'b c' d;
do
echo $var;
done
The bash script loops through 3 arguments printing
a
b c
d
However, if this string is read in via jq , and then looped over like so:
JSON_FILE=path/to/jsonfile.json
ARGUMENTS=$(jq -r '.arguments' "${JSON_FILE}")
for var in ${ARGUMENTS};
do
echo $var;
done
The result is 4 arguments as follows:
a
'b
c'
d
Example json file for reference:
{
"arguments" : "a 'b c' d"
}
What is the reason for this? I tried putting quotes around the variable like suggested in other SO answers but that caused everything to just be handled as 1 argument.
What can I do to get the behavior of the first case (3 arguments)?
What is the reason for this?
The word splitting expansion is run over unquoted results of other expansions. Because ${ARGUMENTS} expansion in for var in ${ARGUMENTS}; is unquoted, word splitting is performed. No, word splitting ignores quotes resulted from variable expansion - it only cares about whitespaces.
What can I do to get the behavior of the first case (3 arguments)?
The good way™ would be to write your own parser, to parse the quotes inside the strings and split the argument depending on the quotes.
I advise to use xargs, it (by default, usually a confusing behavior) parses quotes in the input strings:
$ arguments="a 'b c' d"
$ echo "${arguments}" | xargs -n1 echo
a
b c
d
# convert to array
$ readarray -d '' arr < <(<<<"${arguments}" xargs printf "%s\0")
As presented in the other answer, you may use eval, but please do not, eval is evil and will run expansions over the input string.
Change IFS to a new line to make it work:
...
IFS='\n'; for var in $ARGUMENTS;
do
echo $var;
done

Add prefix/suffix to all elements in space-separated string

Consider you have a shell variable foo, whose value is
echo ${foo}
# Output: elementA elementB elementC
Now I would like to add same prefix __PREFIX__ and suffix __SUFFIX__ to the elements, so that
echo ${new_foo}
# Output: __PREFIX__ElementA__SUFFIX__ __PREFIX__ElementB__SUFFIX__ __PREFIX__ElementC__SUFFIX__
What is the simplest way to achieve that?
Because I'm not sure how such an operation should be called, the title is probably not describing the problem correctly.
Thanks for the comments and answers. The title has been updated.
If you have a proper array,
foo=(a b c)
you can add a prefix using the /# operator and add a suffix with the /% operator. It does have to be done in two steps, though.
$ foo=(a b c)
$ foo=("${foo[#]/#/__PREFIX__}")
$ foo=("${foo[#]/%/__SUFFIX__}")
$ declare -p foo
declare -a foo=([0]="__PREFIX__a__SUFFIX__" [1]="__PREFIX__b__SUFFIX__" [2]="__PREFIX__c__SUFFIX__")
If you just have a space-separated string, you can use //:
$ foo="a b c"
$ foo="__PREFIX__${foo// /__SUFFIX__ __PREFIX__}__SUFFIX__"
$ echo "$foo"
__PREFIX__a__SUFFIX__ __PREFIX__b__SUFFIX__ __PREFIX__c__SUFFIX__
With sed you could do:
prefix=__PREFIX__
suffix=__SUFFIX__
new=$(sed -E "s/(\S)(\s|$)/\1$suffix /g;s/(\s|^)(\S)/$prefix\2/g" <<< $foo)
which outputs:
__PREFIX__elementA__SUFFIX__ __PREFIX__elementB__SUFFIX__ __PREFIX__elementC__SUFFIX__
Here's an easy to read approach, but it's probably the worst from an efficiency standpoint.
foo="elementA elementB elementC"
PREFIX=__PREFIX__
SUFFIX=__SUFFIX__
for f in ${foo}
do
new_foo="${new_foo} ${PREFIX}${f}${SUFFIX}"
done
echo ${new_foo}

Zsh - split string by spaces when using dot operator

Here is my script:
#/bin/bash
list="a b c"
for i in $list; do
echo $i
done
This works:
➜ ~ ./lol.sh
a
b
c
This doesn't:
➜ ~ . ./lol.sh
a b c
Why split does not work with dot command and how can I fix it?
Lists should never be represented as strings. Use array syntax.
list=( a b c )
for i in "${list[#]}"; do
echo "$i"
done
There are several reasons this is preferable.
In ZSH:
ZSH breaks POSIX by not performing string-splitting at all on unquoted expansions unless they explicitly request it. You can make this request by either running setopt sh_word_split, or using the parameter expansions ${=list} or ${(ps: :)list}
In other Bourne-derived shells:
String-splitting is dependent on the value of IFS, which cannot be guaranteed to be at defaults, especially when sourced from a separate script (which may have changed it locally).
Unquoted expansion also performs globbing, which can have different results depending on which files are in the current working directory (for instance, if your list contains hello[world], this will behave in an unexpected manner if your current directory contains files named hellow, helloo, or otherwise matching the glob).
Avoiding the globbing step is not only more correct, but also more efficient.
Whilst I note the comment regarding lists by Charles Duffy, this was my solution/test.
#!/bin/zsh
function three()
{
first=$1
second=$2
third=$3
echo "1: $first 2: $second 3:$third"
}
setopt sh_word_split
set "1 A 2" "2 B 3" "3 C 4" "4 D 5"
for i;do
three $i;
done
This will output
1: 1 2: A 3:2
1: 2 2: B 3:3
1: 3 2: C 3:4
1: 4 2: D 3:5

How to loop through the first n letters of the alphabet in bash

I know that to loop through the alphabet, one can do
for c in {a..z}; do something; done
My question is, how can I loop through the first n letters (e.g. to build a string) where n is a variable/parameter given in the command line.
I searched SO, and only found answers doing this for numbers, e.g. using C-style for loop or seq (see e.g. How do I iterate over a range of numbers defined by variables in Bash?). And I don't have seq in my environment.
Thanks.
The straightforward way is sticking them in an array and looping over that by index:
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
Alternatively, if you just want them dash-separated:
printf "%s-" "${chars[#]:0:n}"
that other guy's answer is probably the way to go, but here's an alternative that doesn't require an array variable:
n=3 # sample value
i=0 # var. for counting iterations
for c in {a..z}; do
echo $c # do something with "$c"
(( ++i == n )) && break # exit loop, once desired count has been reached
done
#rici points out in a comment that you could make do without aux. variable $i by using the conditional (( n-- )) || break to exit the loop, but note that this modifies $n.
Here's another array-free, but less efficient approach that uses substring extraction (parameter expansion):
n=3 # sample value
# Create a space-separated list of letters a-z.
# Note that chars={a..z} does NOT work.
chars=$(echo {a..z})
# Extract the substring containing the specified number
# of letters using parameter expansion with an arithmetic expression,
# and loop over them.
# Note:
# - The variable reference must be _unquoted_ for this to work.
# - Since the list is space-separated, each entry spans 2
# chars., hence `2*n` (you could subtract 1 after, but it'll work either way).
for c in ${chars:0:2*n}; do
echo $c # do something with "$c"
done
Finally, you can combine the array and list approaches for concision, although the pure array approach is more efficient:
n=3 # sample value
chars=( {a..z} ) # create array of letters
# `${chars[#]:0:n}` returns the first n array elements as a space-separated list
# Again, the variable reference must be _unquoted_.
for c in ${chars[#]:0:n}; do
echo $c # do something with "$c"
done
Are you only iterating over the alphabet to create a subset? If that's the case, just make it simple:
$ alpha=abcdefghijklmnopqrstuvqxyz
$ n=4
$ echo ${alpha:0:$n}
abcd
Edit. Based on your comment below, do you have sed?
% sed -e 's/./&-/g' <<< ${alpha:0:$n}
a-b-c-d-
You can loop through the character code of the letters of the alphabet and convert back and forth:
# suppose $INPUT is your input
INPUT='x'
# get the character code and increment it by one
INPUT_CHARCODE=`printf %x "'$INPUT"`
let INPUT_CHARCODE++
# start from character code 61 = 'a'
I=61
while [ $I -ne $INPUT_CHARCODE ]; do
# convert the index to a letter
CURRENT_CHAR=`printf "\x$I"`
echo "current character is: $CURRENT_CHAR"
let I++
done
This question and the answers helped me with my problem, partially.
I needed to loupe over a part of the alphabet based on a letter in bash.
Although the expansion is strictly textual
I found a solution: and made it even more simple:
START=A
STOP=D
for letter in $(eval echo {$START..$STOP}); do
echo $letter
done
Which results in:
A
B
C
D
Hope it's helpful for someone looking for the same problem i had to solve,
and ends up here as well
(also answered here)
And the complete answer to the original question is:
START=A
n=4
OFFSET=$( expr $(printf "%x" \'$START) + $n)
STOP=$(printf "\x$OFFSET")
for letter in $(eval echo {$START..$STOP}); do
echo $letter
done
Which results in the same:
A
B
C
D

Looping through alphabets in Bash

I want to mv all the files starting with 'x' to directory 'x'; something like:
mv path1/x*.ext path2/x
and do it for all alphabet letters a, ..., z
How can I write a bash script which makes 'x' loops through the alphabet?
for x in {a..z}
do
echo "$x"
mkdir -p path2/${x}
mv path1/${x}*.ext path2/${x}
done
This should get you started:
for letter in {a..z} ; do
echo $letter
done
here's how to generate the Spanish alphabet using nested brace expansion
for l in {{a..n},ñ,{o..z}}; do echo $l ; done | nl
1 a
...
14 n
15 ñ
16 o
...
27 z
Or simply
echo -e {{a..n},ñ,{o..z}}"\n" | nl
If you want to generate the obsolete 29 characters Spanish alphabet
echo -e {{a..c},ch,{d..l},ll,{m,n},ñ,{o..z}}"\n" | nl
Similar could be done for French alphabet or German alphabet.
Using rename:
mkdir -p path2/{a..z}
rename 's|path1/([a-z])(.*)|path2/$1/$1$2' path1/{a..z}*
If you want to strip-off the leading [a-z] character from filename, the updated perlexpr would be:
rename 's|path1/([a-z])(.*)|path2/$1/$2' path1/{a..z}*
With uppercase as well
for letter in {{a..z},{A..Z}}; do
echo $letter
done
This question and the answers helped me with my problem, partially.
I needed to loupe over a part of the alphabet in bash.
Although the expansion is strictly textual
I found a solution: and made it even more simple:
START=A
STOP=D
for letter in $(eval echo {$START..$STOP}); do
echo $letter
done
Which results in:
A
B
C
D
Hope its helpful for someone looking for the same problem i had to solve,
and ends up here as well
I hope this can help.
for i in {a..z}
for i in {A..Z}
for i in {{a..z},{A..Z}}
use loop according to need.

Resources