Storing Result in Array and Substring in Bash - bash

I have the list of committed files on svn stored in a variable as the following:
REPOS="$1"
TXN="$2"
DETAILED_FILES=`svnlook changed -t $TXN $REPOS`
DETAILED_FILES looks like:
U data0.xml A data1.xml UU all_data.xml
How can I remove all change type prefixes? such as U |data0.xml
Also, is it possible to store these in an array?
And can I get the full path of these files by svnlook?

A more proper way would be:
repos=$1
txn=$2
files=()
while read -r _ f; do
files+=( "$f" )
done < <(svnlook -t "$txn" "$repos")
Mind the quotes! (you used quotes where they are useless—yet harmless—but omitted the mandatory ones!).

Yes, just do:
FILES=( $(echo $DETAILED_FILES | cut -c 3-) )
Now FILES is an array and you can access the array elements by iterating over them:
for i in "${FILES[#]}"; do echo "$i"; done
Explicitly, ${FILES[0]} will get you the first element, ${FILES[1]} second and so on.
I am not familiar with svnlook so can not answer your second question.

Related

How do i add whitepsaces to a String while filling it up in a for-loop in Bash?

Have a string as follows:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
Want to extract the first two directories and save it as another string like this:
"applications/dbt applications/dbt applications/dataform pplications/dataform"
But while filling up the second string, its being saved as
applications/dbtapplications/dbtapplications/dataformapplications/dataform
What i tried:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
arr=($files)
#extracting the first two directories and saving it to a new string
for i in ${arr[#]}; do files2+=$(echo "$i" | cut -d/ -f 1-2); done
echo $files2
files2 echoes the following
applications/dbtapplications/dbtapplications/dataformapplications/dataform
Reusing your code as much as possible:
(assuming to only remove the last right part):
arr=( applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml )
#extracting the first two directories and saving it to a new string
for file in "${arr[#]}"; do
files2+="${file%/*} "
done
echo "$files2"
applications/dbt applications/dbt applications/dataform
You could use a for loop as requested
for dir in ${files};
do file2+=$(printf '%s ' "${dir%/*}")
done
which will give output
$ echo "$file2"
applications/dbt applications/dbt applications/dataform applications/dataform
However, it would be much easier with sed
$ sed -E 's~([^/]*/[^/]*)[^ ]*~\1~g' <<< $files
applications/dbt applications/dbt applications/dataform applications/dataform
Convert the string in an array first. Assuming there are no white/blank/newline space embedded in your strings/path name. Something like
#!/usr/bin/env bash
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
mapfile -t array <<< "${files// /$'\n'}"
Now check the value of the array
declare -p array
Output
declare -a array=([0]="applications/dbt/Dockerfile" [1]="applications/dbt/cloudbuild.yaml" [2]="applications/dataform/Dockerfile" [3]="applications/dataform/cloudbuild.yaml")
Remove all the last / from the path name in the array.
new_array=("${array[#]%/*}")
Check the new value
declare -p new_array
Output
declare -a new_array=([0]="applications/dbt" [1]="applications/dbt" [2]="applications/dataform" [3]="applications/dataform")
Now the value is an array, assign it to a variable or do what ever you like with it. Like what was mentioned in the comment section. Use an array from the start.
Assign the first 2 directories/path in a variable (weird requirement)
new_var="${new_array[#]::2}"
declare -p new_var
Output
declare -- new_var="applications/dbt applications/dbt"

How to rename files with incrementing numbers to files with that number plus 10

Hi I have a list of files ex. 0.png, 1.png ... 60.png, 61.png and I want to rename all the files to 10.png,11.png ... 70.png, 71.png however I do not know how I could do that.
In bash, you can use a parameter expansion to handle the rename, e.g.
for name in *.png; do
val="${name%.png}"
val=$((val+10))
mv "$name" "$val.png"
done
Explanation
val is created from the parameter expansion "${name%.png}" which simply trims ".png" from the right-hand side of the filename.
val=$((val+10)) adds 10 to the number.
mv "$name" "$val.png" moves the file from its original name to the new name with the value increased by 10.
If you want to eliminate the intermediate val variable, you can do it all in a single expression, e.g.
for name in *.png; do
mv "$name" "$((${name%.png} + 10)).png"
done
Look things over and let me know if you have further questions.
Assuming that the filenames are of the form number.ext, this function would do the trick.
#!/bin/bash
function rename_file() {
local file=$1
local fname=$(($(echo $file | cut -d. -f1) + 10))
local ext=$(echo $file | cut -d. -f2)
mv $file $fname.$ext
}
To rename a file, call rename_file file_name in your shell script.

Bash: how to turn list of unique no data values into variables that can be used?

basepath=Desktop/DEM
dir=(ls -1 type -f)
cd $dir
for f in *.tif; do gdalinfo "$f" | grep -o 'NoData Value\=[-0-9]*' || echo "NoData Value=None"; done > test.txt
cat test.txt | sort | uniq > uniquenodata.txt #this is to find unique no data values in a directory
nodatalist=$(cat uniquenodata.txt)
rightnodata=-9999
I have made the BASH script above to find out the different no data values in a directory.
My goal is to have separate folders that have only one type of no data value, I need to somehow create a for loop that will convert the list of unique no data values ($nodatalist) and check each tif's no data value and send it to the corresponding folder that has these no data values. I am very new to BASH and do not know how to turn a list of values into a variable that can be used in a for loop.
A more efficient approach is to move the files immediately. Create the destination directory if it doesn't exist.
for f in *.tif; do
i=$(gdalinfo "$f" | grep -o 'NoData Value=[-0-9]*') && d=${i#NoData Value=} || d="None"
mkdir -p "$d"
mv "$f" "$d"/
done
As an aside, these lines look like a syntax error:
dir=(ls -1 type -f)
cd $dir
This will effectively cd test if you have a directory by this name. Maybe you actually mean find -type f but this obviously doesn't produce a directory (-type f specifically selects regular files which aren't directories).
You can use variable indirection, a variable whose value is the name of another variable
for d in "${nodatalist[#]}"; do
echo "${!d}"
done
As this example illustrates
declare -a a=("b" "c" "d");
b=1
c=2
d=3
for i in "${a[#]}"; do
echo "name: $i, value: ${!i}"
done
Output:
name: b, value: 1
name: c, value: 2
name: d, value: 3

Find pairs of files in one directory with a specific pattern

I need to find pairs of files with a specific pattern in one directory:
HU_IP_number_something.bam & HU_inp_number_something.bam
NOC_IP_number_something.bam & NOC_inp_number_something.bam
Numbers are 1...N for each pair
I have a solution but it works only for one set of files HU_* or NOC_* in one directory.
How can I improve it to find pairs, when both HU_* and NOC_* are in one directory?
for ip in *IP*.bam
do
num=$(echo $ip | sed 's/[^0-9]//g')
input=$(find -name *_inp_${num}*.bam)
echo ip sample: $ip
echo input sample: $input
done
Examples of files in one directory:
HU_inp_1-sorted.bam
HU_IP_1-sorted.bam
NOC_inp_1-sorted.bam
NOC_IP_1-sorted.bam
for 1,2,3,...N
The following builds an array, $a for each iteration of a for loop.
$ for f in *IP*.bam; do s=${f#*_}; a=( *${s} ); declare -p a; done
declare -a a=([0]="HU_IP_number_something.bam" [1]="NOC_IP_number_something.bam")
declare -a a=([0]="HU_IP_number_something.bam" [1]="NOC_IP_number_something.bam")
This works steps through all the files you've specified in your filespec, stripping off the first "field" (as denoted by the underscore separator), and using globbing to collect the relevant files in the array.
You can test for the length of the array (${#a[#]}) to make sure you have two entries.
If you want to group by the second field instead of the first, you need a little more processing:
$ for f in *IP*.bam; do s1=${f%%_*}; s2=${f#*_}; s2=${s2#*_}; a=( ${s1}*${s2} ); declare -p a; done
declare -a a=([0]="HU_IP_number_something.bam" [1]="HU_inp_number_something.bam")
declare -a a=([0]="NOC_IP_number_something.bam" [1]="NOC_inp_number_something.bam")
The technique here, using ${var#pattern} and ${var%pattern} is called Parameter Expansion, and you can find more details about it in the bash man page. Here too.
Do you only want to match HU to HU and NOC to NOC? If so:
If you add a line
pre=$(echo $ip | awk -F "_" '{print $1}')
then change you input to
input=$(find -name $pre_inp_${num}*.bam)

Open file with two columns and dynamically create variables

I'm wondering if anyone can help. I've not managed to find much in the way of examples and I'm not sure where to start coding wise either.
I have a file with the following contents...
VarA=/path/to/a
VarB=/path/to/b
VarC=/path/to/c
VarD=description of program
...
The columns are delimited by the '=' and some of the items in the 2nd column may contain gaps as they aren't just paths.
Ideally I'd love to open this in my script once and store the first column as the variable and the second as the value, for example...
echo $VarA
...
/path/to/a
echo $VarB
...
/path/to/a
Is this possible or am I living in a fairy land?
Thanks
You might be able to use the following loop:
while IFS== read -r name value; do
declare "$name=$value"
done < file.txt
Note, though, that a line like foo="3 5" would include the quotes in the value of the variable foo.
A minus sign or a special character isn't allowed in a variable name in Unix.
You may consider using BASH associative array for storing key and value together:
# declare an associative array
declare -A arr
# read file and populate the associative array
while IFS== read -r k v; do
arr["$k"]="$v"
done < file
# check output of our array
declare -p arr
declare -A arr='([VarA]="/path/to/a" [VarC]="/path/to/c" [VarB]="/path/to/b" [VarD]="description of program" )'
What about source my-file? It won't work with spaces though, but will work for what you've shared. This is an example:
reut#reut-home:~$ cat src
test=123
test2=abc/def
reut#reut-home:~$ echo $test $test2
reut#reut-home:~$ source src
reut#reut-home:~$ echo $test $test2
123 abc/def

Resources