Double sequence with brackets? - bash

I was wondering if there is a simple way to produce a double sequence with something similar to curl braces.
I would like to produce a double sequence like this one:
eog directory1/somethingelse/file2.png directory3/somethingelse/file6.png ... directory25/somethingelse/file50.png
The sequences of directories and files are regular (I mean is something like, e.g., {1..25..2} for directories and {2..50..4} for files).
I wonder if there is a simple way to produce the sequences instead of using vectors with all the values. I mean something like
eog directory[someOpenedBracket]1..25..2[someClosedBracket]/somethingelse/file[someOpenedBracket]2..50..4[someClosedBracket].png
Thanks in advance

You can populate 2 separate array and loop through them:
dirs=(directory{1..25..2})
files=(file{2..50..4})
for ((i=0; i<${#dirs[#]}; i++)); do
printf '%s ' "${dirs[i]}/somethingelse/${files[i]}.png"
done
echo
Output: (appears in one line in output)
directory1/somethingelse/file2.png
directory3/somethingelse/file6.png
directory5/somethingelse/file10.png
directory7/somethingelse/file14.png
directory9/somethingelse/file18.png
directory11/somethingelse/file22.png
directory13/somethingelse/file26.png
directory15/somethingelse/file30.png
directory17/somethingelse/file34.png
directory19/somethingelse/file38.png
directory21/somethingelse/file42.png
directory23/somethingelse/file46.png
directory25/somethingelse/file50.png

I think this is all you can achieve with Bash.
eog $(i=0; while ((++i<=25)); do echo dir$i/file$((i++*2)); done)

Related

How to place files containing increasing numeric names consecutively in the terminal

I have certain files named something like file_1.txt, file_2.txt, ..., file_40.txt and I want to plot them in the terminal using xmgrace like this:
xmgrace file_01.txt file_02.txt [...] file_40.txt
What would be a bash code, maybe a for loop code so that I don't have to write them one by one from 1 to 40, please?
[Edit:]
I should mention that I tried to use the for loop as follows: for i in {00-40}; do xmgrace file_$i.txt; done, but it didn't help as it opens each file separately.
Depending of the tool you use:
xmlgrace file_*.txt
using a glob (this will treat all files matching the pattern)
or as Jetchisel wrote in comments:
xmlgrace file_{1..40}.txt
This is brace expansion
For general purpose, if the tool require a loop:
for i in {1..40}; do something "$i"; done
or
for ((i=0; i<=40; i++)); do something "$i"; done

concatenate files with similar names using shell

I have very limited knowledge of shell scripting, for example if I have the following files in a folder
abcd_1_1.txt
abcd_1_2.txt
def_2_1.txt
def_2_2.txt
I want the output as abcd_1.txt, def_2.txt. For each pattern in the file names, concantenate the files and generate the 'pattern'.txt as an output
patterns list <-?
for i in patterns; do echo cat "$i"* > "$i".txt; done
I am not sure how to code this in a shell script, any help is appreciated.
Maybe something like this (assumes bash, and I didn't test it).
declare -A prefix
files=(*.txt)
for f in "${files[#]"; do
prefix[${f%_*}]=
done
for key in "${!prefix[#]}"; do
echo "${prefix[$key]}.txt"
done
for i in abcd_1 def_2
do
cat "$i"*.txt > "$i".txt
done
The above will work in any POSIX shell, such as dash or bash.
If, for some reason, you want to maintain a list of patterns and then loop through them, then it is appropriate to use an array:
#!/bin/bash
patterns=(abcd_1 def_2)
for i in "${patterns[#]}"
do
cat "$i"*.txt > "$i".txt
done
Arrays require an advanced shell such as bash.
Related Issue: File Order
Does it the order in which files are added to abcd_1 or def_2 matter to you? The * will result is lexical ordering. This can conflict with numeric ordering. For example:
$ echo def_2_*.txt
def_2_10.txt def_2_11.txt def_2_12.txt def_2_1.txt def_2_2.txt def_2_3.txt def_2_4.txt def_2_5.txt def_2_6.txt def_2_7.txt def_2_8.txt def_2_9.txt
Observe that def_2_12.txt appears in the list ahead of def_2_1.txt. Is this a problem? If so, we can explicitly force numeric ordering. One method to do this is bash's brace expansion:
$ echo def_2_{1..12}.txt
def_2_1.txt def_2_2.txt def_2_3.txt def_2_4.txt def_2_5.txt def_2_6.txt def_2_7.txt def_2_8.txt def_2_9.txt def_2_10.txt def_2_11.txt def_2_12.txt
In the above, the files are numerically ordered.

printf %d is adding instead of replacing padded zeros

Long story short: printf is adding zeros to a number instead of having the number replace them.
More details:
I'm currently attempting to use Bash on my Ubuntu OS to execute a series of broken up files
containing queries for my database.
The files were broken up to look like the following:
x0000000000
x0000000001
x0000000002
etc.
I was making a small script that will run these files by using the padding zeros function of Bash's printf.
My script looks like every one I've Google'd and seen, but the results are different.
Here's my script: (Not the one for the queries, but the one to get the file name)
for i in {0..10};
do
printf "x%010d"$i;
done
Which should come out to something likes this:
x0000000000
x0000000001
x0000000002
x0000000003
x0000000004
x0000000005
x0000000006
x0000000007
x0000000008
x0000000009
x0000000010
But is instead coming out like this:
x00000000000
x00000000001
x00000000002
x00000000003
x00000000004
x00000000005
x00000000006
x00000000007
x00000000008
x00000000009
x000000000010
It's purely adding zeros to the number instead of the number replacing them.
Can someone please tell me if I unintentionally turned on some setting, did something wrong, or have a bug?
You need a space, otherwise concatenation occurs:
printf "x%010d" $i;
^

BASH Expression to replace beginning and ending of a string in one operation?

Here's a simple problem that's been bugging me for some time. I often find I have a number of input files in some directory, and I want to construct output file names by replacing beginning and ending portions. For example, given this:
source/foo.c
source/bar.c
source/foo_bar.c
I often end up writing BASH expressions like:
for f in source/*.c; do
a="obj/${f##*/}"
b="${a%.*}.obj"
process "$f" "$b"
done
to generate the commands
process "source/foo.c" "obj/foo.obj"
process "source/bar.c "obj/bar.obj"
process "source/foo_bar.c "obj/foo_bar.obj"
The above works, but its a lot wordier than I like, and I would prefer to avoid the temporary variables. Ideally there would be some command that could replace the beginning and ends of a string in one shot, so that I could just write something like:
for f in source/*.c; do process "$f" "obj/${f##*/%.*}.obj"; done
Of course, the above doesn't work. Does anyone know something that will? I'm just trying to save myself some typing here.
Not the prettiest thing in the world, but you can use a regular expression to group the content you want to pick out, and then refer to the BASH_REMATCH array:
if [[ $f =~ ^source/(.*).c$ ]] ; then f="obj/${BASH_REMATCH[1]}.o"; fi
you shouldn't have to worry about your code being "wordier" or not. In fact, being a bit verbose is no harm, consider how much it will improve your(or someone else) understanding of the script. Besides, for performance, using bash's internal string manipulation is much faster than calling external commands. Lastly, you are not going to retype your commands every time you use it right? So why worry that its "wordier" since these commands are already in your script?
Not directly in bash. You can use sed, of course:
b="$(sed 's|^source/(.*).c$|obj/$1.obj|' <<< "$f")"
Why not simply using cd to remove the "source/" part?
This way we can avoid the temporary variables a and b:
for f in $(cd source; printf "%s\n" *.c); do
echo process "source/${f}" "obj/${f%.*}.obj"
done

How to rename files keeping a variable part of the original file name

I'm trying to make a script that will go into a directory and run my own application with each file matching a regular expression, specifically Test[0-9]*.txt.
My input filenames look like this TestXX.txt. Now, I could just use cut and chop off the Test and .txt, but how would I do this if XX wasn't predefined to be two digits? What would I do if I had Test1.txt, ..., Test10.txt? In other words, How would I get the [0-9]* part?
Just so you know, I want to be able to make a OutputXX.txt :)
EDIT:
I have files with filename Test[0-9]*.txt and I want to manipulate the string into Output[0-9]*.txt
Would something like this help?
#!/bin/bash
for f in Test*.txt ;
do
process < $f > ${f/Test/Output}
done
Bash Shell Parameter Expansion
A good tutorial on regexes in bash is here. Summarizing, you need something like:
if [[$filenamein =~ "^Test([0-9]*).txt$"]]; then
filenameout = "Output${BASH_REMATCH[1]}.txt"
and so on. The key is that, when you perform the =~" regex-match, the "sub-matches" to parentheses-enclosed groups in the RE are set in the entries of arrayBASH_REMATCH(the[0]entry is the whole match,1` the first parentheses-enclosed group, etc).
You need to use rounded brackets around the part you want to keep.
i.e. "Test([0-9]*).txt"
The syntax for replacing these bracketed groups varies between programs, but you'll probably find you can use \1 , something like this:
s/Test(0-9*).txt/Output\1.txt/
If you're using a unix shell, then 'sed' might be your best bet for performing the transformation.
http://www.grymoire.com/Unix/Sed.html#uh-4
Hope that helps
for file in Test[0-9]*.txt;
do
num=${file//[^0-9]/}
process $file > "Output${num}.txt"
done

Resources