Twice Bash command substitution - linux

I have directories a1..a5, b1..b5 and c1..c5. Inside each directory I have two files a1, b1 and c1.
do mkdir /tmp/{a,b}$d; touch /tmp/{a,b,c}$d/{a,b,c}1; done;
I want to get all the files starting with 'a' or 'b' inside the directories starting with an 'a'. I can do it with:
DIRS=`ls -1 -d /tmp/{a,b}*/a*`
echo ${DIRS}
and obtain:
/tmp/a1/a1 /tmp/a2/a1 /tmp/a3/a1 /tmp/a4/a1 /tmp/a5/a1
/tmp/b1/a1 /tmp/b2/a1 /tmp/b3/a1 /tmp/b4/a1 /tmp/b5/a1
Now, I will use a variable called DATA to store the directories and later get the files:
DATA="/tmp/{a,b}*"
echo ${DATA}
DIRS=`ls -1 -d ${DATA}/a*`
echo ${DIRS}
In the output, the DATA contents is OK (/tmp/{a,b}*), but I receive the following error:
ls: cannot access /tmp/{a,b}*/a*: No such file or directory
Any idea why this happens?

I solved the problem, but I can't find any reference about why my previous attempts failed.
DATA="/tmp/{a,b}*"
echo ${DATA}
DIRS=`eval "ls -1 -d ${DATA}/a*"`
echo ${DIRS}
Output:
/tmp/a1/a1 /tmp/a2/a1 /tmp/a3/a1 /tmp/a4/a1 /tmp/a5/a1 /tmp/b1/a1
/tmp/b2/a1 /tmp/b3/a1 /tmp/b4/a1 /tmp/b5/a1

Related

How can I match two sections of a filename to list the files that match it?

I have a bunch of files that need merging.
The name of the files are something like this-
JOHN_80_xyz_yeti.txt
JOHN_80_xyz_puma.txt
JOHN_80_def_yeti.txt
JOHN_80_def_puma.txt
JOHN_81_xyz_yeti.txt
JOHN_81_xyz_puma.txt
JOHN_81_def_yeti.txt
JOHN_81_def_puma.txt
JOHN_82_xyz_yeti.txt
JOHN_82_xyz_puma.txt
JOHN_82_def_yeti.txt
JOHN_82_def_puma.txt
JOHN_83_xyz_yeti.txt
JOHN_83_xyz_puma.txt
JOHN_83_def_yeti.txt
JOHN_83_def_puma.txt
I want to merge JOHN_80_xyz_yeti.txt and JOHN_80_def_yeti.txt; JOHN_80_xyz_puma.txt and JOHN_80_def_puma.txt; JOHN_81_xyz_yeti.txt and JOHN_81_def_yeti.txt; JOHN_81_xyz_puma.txt and JOHN_81_def_puma.txt; and so forth, recursively through my files in a bash for loop. What command can I use so that it finds the files that have "80" and "yeti" together and list/echo it as a variable to be used in a for loop?
The command that I want to use these files for is given below-
merge -1 JOHN_80_xyz_yeti.txt -2 JOHN_80_def_yeti.txt > merged.JOHN_80_yeti.txt
merge -1 JOHN_80_xyz_puma.txt -2 JOHN_80_def_puma.txt > merged.JOHN_80_puma.txt
I tried "find file name" but failed to get the desired results.
Loop through all the xyz files and use string substitution to replace xyz with def, and replace the entire suffix with chewbacca.
for xyzfile in *_xyz_*.txt; do
deffile=${file/_xyz_/_def_}
result=${file/_xyz_*/_chewbaccca.txt}
merge -1 "$xyzfile" -2 "$deffile" > "$result"
Assuming you want to dynamically collect the substrings -
mkdir -p merged # backup
for f in JOHN*txt # loop though glob
do IFS=_ read a b x c <<< "$f" # parse the filename
lst=( $a*$b*$c ) # get its match (always only one?)
[[ -e $lst ]] || continue # skip if already done (this is 2nd file)
merge -1 "${lst[0]}" -2 "${lst[1]}" > merged.${a}_${b}_${c} # do the thing.
mv "${lst[#]}" merged/ # move to backup
done
You can verify and then delete the merged folder, or undo/edit/redo till it's right.

Concatenate a string with an array for recursively copy file in bash

I have a concatenation problem between a string and an array
I want to copy all the files contained in the directories stored in the array, my command is in a loop (to recursively copy my files)
yes | cp -rf "./$WORK_DIR/${array[$i]}/"* $DEST_DIR
My array :
array=("My folder" "...")
I have in my array several folder names (they have spaces in their names) that I would like append to my $WORK_DIR to make it possible to copy the files for cp.
But I always have the following error
cp: impossible to evaluate './WORKDIR/my': No such files or folders
cp: impossible to evaluate 'folder/*': No such files or folders
This worked for me
#!/bin/bash
arr=("My folder" "This is a test")
i=0
while [[ ${i} -lt ${#arr[#]} ]]; do
echo ${arr[${i}]}
cp -rfv ./source/"${arr[${i}]}"/* ./dest/.
(( i++ ))
done
exit 0
I ran the script. It gave me the following output:
My folder
'./source/My folder/blah-folder' -> './dest/./blah-folder'
'./source/My folder/foo-folder' -> './dest/./foo-folder'
This is a test
'./source/This is a test/blah-this' -> './dest/./blah-this'
'./source/This is a test/foo-this' -> './dest/./foo-this'
Not sure of the exact difference, but hopefully this will help.

bash: How to transfer/copy only the file names to separate similar files?

I've some files in a folder A which are named like that:
001_file.xyz
002_file.xyz
003_file.xyz
in a separate folder B I've files like this:
001_FILE_somerandomtext.zyx
002_FILE_somerandomtext.zyx
003_FILE_somerandomtext.zyx
Now I want to rename, if possible, with just a command line in the bash all the files in folder B with the file names in folder A. The file extension must stay different.
There is exactly the same amount of files in each folder A and B and they both have the same order due to numbering.
I'm a total noob, but I hope some easy answer for the problem will show up.
Thanks in advance!
ZVLKX
*Example edited for clarification
An implementation might look a bit like this:
renameFromDir() {
useNamesFromDir=$1
forFilesFromDir=$2
for f in "$forFilesFromDir"/*; do
# Put original extension in $f_ext
f_ext=${f##*.}
# Put number in $f_num
f_num=${f##*/}; f_num=${f_num%%_*}
# look for a file in directory B with same number
set -- "$useNamesFromDir"/"${f_num}"_*.*
[[ $1 && -e $1 ]] || {
echo "Could not find file number $f_num in $dirB" >&2
continue
}
(( $# > 1 )) && {
# there's more than one file with the same number; write an error
echo "Found more than one file with number $f_num in $dirB" >&2
printf ' - %q\n' "$#" >&2
continue
}
# extract the parts of our destination filename we want to keep
destName=${1##*/} # remove everything up to the last /
destName=${destName%.*} # and past the last .
# write the command we would run to stdout
printf '%q ' mv "$f" "$forFilesFromDir/$destName.$f_ext"; printf '\n'
## or uncomment this to actually run the command
# mv "$f" "$forFilesFromDir/$destName.$f_ext"
done
}
Now, how would we test this?
mkdir -p A B
touch A/00{1,2,3}_file.xyz B/00{1,2,3}_FILE_somerandomtext.zyx
renameFromDir A B
Given that, the output is:
mv B/001_FILE_somerandomtext.zyx B/001_file.zyx
mv B/002_FILE_somerandomtext.zyx B/002_file.zyx
mv B/003_FILE_somerandomtext.zyx B/003_file.zyx
Sorry if this isn't helpful, but I had fun writing it.
This renames items in folder B to the names in folder A, preserving the extension of B.
A_DIR="./A"
A_FILE_EXT=".xyz"
B_DIR="./B"
B_FILE_EXT=".zyx"
FILES_IN_A=`find $A_DIR -type f -name *$A_FILE_EXT`
FILES_IN_B=`find $B_DIR -type f -name *$B_FILE_EXT`
for A_FILE in $FILES_IN_A
do
A_BASE_FILE=`basename $A_FILE`
A_FILE_NUMBER=(${A_BASE_FILE//_/ })
A_FILE_WITHOUT_EXTENSION=(${A_BASE_FILE//./ })
for B_FILE in $FILES_IN_B
do
B_BASE_FILE=`basename $B_FILE`
B_FILE_NUMBER=(${B_BASE_FILE//_/ })
if [ ${A_FILE_NUMBER[0]} == ${B_FILE_NUMBER[0]} ]; then
mv $B_FILE $B_DIR/$A_FILE_WITHOUT_EXTENSION$B_FILE_EXT
break
fi
done
done

Why does script not recognize file extension?

My script
#!/bin/bash
cp *.ats /home/milenko/procmt
mycd() {
cd /home/milenko/procmt
}
mycd
EXT=ats
for i in *; do
if [ "${i}" != "${i%.${EXT}}" ];then
./tsmp -ascii i
fi
done
But
milenko#milenko-HP-Compaq-6830s:~/Serra do Mel/MT06/meas_2016-07-13_20-22-00$ bash k1.sh
./tsmp: handling 1 files ************************************** total input channels: 1
the name of your file does not end with ats ... might crash soon
main (no rda) -> can not open i for input, exit
./tsmp: handling 1 files ************************************** total input channels: 1
the name of your file does not end with ats ... might crash soon
main (no rda) -> can not open i for input, exit
When I go to procmt directory and list files
milenko#milenko-HP-Compaq-6830s:~/procmt$ ls *.ats
262_V01_C00_R000_TEx_BL_2048H.ats 262_V01_C00_R086_TEx_BL_4096H.ats 262_V01_C02_R000_THx_BL_2048H.ats
262_V01_C00_R000_TEx_BL_4096H.ats 262_V01_C01_R000_TEy_BL_2048H.ats 262_V01_C03_R000_THy_BL_2048H.ats
What is wrong with my script?
If I understand correctly this should work for you:
dest='/home/milenko/procmt'
cp *.ats "$dest"
cd "$dest"
for i in *.ats; do
./tsmp -ascii "$i"
done
There is no need to loop through all files when you're only interested in .ats files. Your mycd function is just doing cd so you can avoid that as well.

Bash merge columned files into one file with rows

I have many data files in this format:
-1597.5421
-1909.6982
-1991.8743
-2033.5744
But I would like to merge them all into one data file with each original data file taking up one row with spaces in between so I can import it in excel.
-1597.5421 -1909.6982 -1991.8743 -2033.5744
-1789.3324 -1234.5678 -9876.5433 -9999.4321
And so on. Each file is named ALL.ene and every directory in my working directory contains it. Can someone give me a quick fix? Thanks!
:edit. Each file has 11 entries. Those were just examples.
for i in */ALL.ene
do
echo $(<$i)
done > result.txt
Assumptions:
I assume all your data files are of this format:
<something1><newline>
<something2><newline>
<something3><newline>
So for example, if the last newline is missing, the following script will miss the field corresponding to <something3>.
Usage: ./merge.bash -o <output file> <input file list or glob>
The script appends to any existing output files from previous runs. It also does not make any assumptions to how many fields of data every input file has. It blindly puts every line into a line in the output file separated by spaces.
#!/bin/bash
# set -o xtrace # uncomment to debug
declare output
[[ $1 =~ -o$ ]] && output="$2" && shift 2 || { \
echo "The first argument should always be -o <output>";
exit -1; }
declare -a files=("${#}") row
for file in "${files[#]}";
do
while read data; do
row+=("$data")
done < "$file"
echo "${row[#]}" >> "$output"
row=()
done
Example:
$ cat data1
-1597.5421
-1909.6982
-1991.8743
-2033.5744
$ cat data2
-1789.3324
-1234.5678
-9876.5433
-9999.4321
$ ./merge.bash -o test data{1,2}
$ cat test
-1597.5421 -1909.6982 -1991.8743 -2033.5744
-1789.3324 -1234.5678 -9876.5433 -9999.4321
This is what coreutils paste is good at, try:
paste -s data_files*

Resources