Translate Array with Array - arrays

I search a way to translate a key from one array with other array.
tmp_title=$3
title=$(echo ${tmp_title,,} | sed -e 's/\s/-/g')
tags=(computer media state society)
de=(computer medien staat gesellschaft)
fr=(ordinateur journalisme politique société)
ru=(Компьютер СМИ штат общество)
file="./content/de/blog/$(date +"%Y")/$(date +"%m")/$title.md"
if test -n "$2"; then
# check tag is in tags array
if [[ ${tags[*]} =~ $2 ]]; then
# check the folder structure is right
if [[ -d ./content/de/blog/$(date +"%Y")/$(date +"%m") ]]; then
# create the content and fill up the file
echo "---" >> "$file"
echo "title: \"$3\"" >> "$file"
echo "date: $(date +\"%Y-%m-%d\")" >> "$file"
echo "draft: false" >> "$file"
echo "tags: \"$2\"" >> "$file"
echo "shorttext:" >> "$file"
echo "cover: \"$2\"" >> "$file"
echo "lang: $1" >> "$file"
echo "---" >> "$file"
fi
else
echo "Enter a valid tag name ..."
fi
fi
I search a way to translate "tags: \"$2\"" >> "$file" in the language array value. When I append society to the script then should be tags: "gesellschaft".
Thank you for help.
Silvio

Consider this approach
case $LANG in
*en*) tags=(computer media state society );;
*de*) tags=(computer medien staat gesellschaft);;
*fr*) tags=(ordinateur journalisme politique société );;
*ru*) tags=(Компьютер СМИ штат общество );;
esac

I have it realize other. I ask for categories and translate then to the $lang value.
#!/usr/bin/env bash
# variables through user input
lang=$1
cover=$2
tmp_title=$3
# variables which we use in script
# create a title with small letters and remove whitespace
title=$(echo ${tmp_title,,} | sed -e 's/\s/-/g')
# categories
categories=(computer media repression society)
# date variables
date=$(date +"%Y-%m-%d")
year=$(date +"%Y")
month=$(date +"%m")
# content variables
content_dir="./content/$lang/blog/$year/$month"
file="$content_dir/$title.md"
# function
function create_file()
{
{
echo "---"
echo "title: \"$tmp_title\""
echo "date: $date"
echo "draft: false"
echo "tags: \"$tag\""
echo "shorttext:"
echo "cover: \"$cover\""
echo "lang: $lang"
echo "---"
} >> "$file"
}
case $1 in
de)
if test -n "$lang"; then
# check tag is in array
if [[ ${categories[*]} =~ $cover ]]; then
# translation tag > categories
if [[ $cover =~ "computer" ]]; then
tag="Computer"
elif [[ $cover =~ "media" ]]; then
tag="Medien"
elif [[ $cover =~ "repression" ]]; then
tag="Staat"
elif [[ $cover =~ "society" ]]; then
tag="Gesellschaft"
fi
# check the folder structure is right
if [[ -d "$content_dir" ]]; then
# create the content and fill up the file
create_file
if [[ -f "$file" ]]; then
subl "$file"
fi
else
# create the folder of content
mkdir -p "$content_dir"
# create the content and fill up the file
create_file
if [[ -f "$file" ]]; then
subl "$file"
fi
fi
else
echo "Enter a valid tag name ..."
fi
fi
;;
en)
if test -n "$lang"; then
# check tag is in array
if [[ ${categories[*]} =~ $cover ]]; then
# translation tag > categories
if [[ $cover =~ "computer" ]]; then
tag="Computer"
elif [[ $cover =~ "media" ]]; then
tag="Media"
elif [[ $cover =~ "repression" ]]; then
tag="State"
elif [[ $cover =~ "society" ]]; then
tag="Society"
fi
# check the folder structure is right
if [[ -d "$content_dir" ]]; then
# create the content and fill up the file
create_file
if [[ -f "$file" ]]; then
subl "$file"
fi
else
# create the folder of content
mkdir -p "$content_dir"
# create the content and fill up the file
create_file
if [[ -f "$file" ]]; then
subl "$file"
fi
fi
else
echo "Enter a valid tag name ..."
fi
fi
;;
info)
echo "To work with this script you need append the follow stuff"
echo "./bin/new.sh lang cover title"
echo "./bin/news.sh en society 'This is a title'"
echo "cover: computer, media, repression, society"
;;
esac
Silvio

Related

Bash - How to check if value doesn't exist in array?

Folks,
I have an array, ex,
declare -a arr=("crm" "hr" "pos")
I need to output error if the passed value doesn't exist in this array
I'm trying this use below snippet but it prints "No match found" for any value
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 1 ]]; then
echo "No match found"
fi
Any idea how to loop in array and popup error if value doesn't exist ?
There are already answers about your question see check value is in an array, but a fix/idea for your specific approach is something like.
#!/usr/bin/env bash
declare -a arr=("crm" "hr" "pos")
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
((match++))
fi
done
if (( match == ${#arr[*]} )); then
printf >&2 "No match found\n"
fi
The above script increments match every time the test inside the for loop is true, and in the end compare match with the length of the array ${#arr[*]}. A more verbose output is to put set -x on after the shebang and add a $ sign to the variable match (which is not required) inside the (( )).
if (( $match == ${#arr[*]} )); then
Your original approach always breaks the loop when it does not have a match, doing so will not continue the loop.
You reversed the logic. Just leave out the ! and swap the final test:
match=0
for i in "${arr[#]}"; do
if [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 0 ]]; then
echo "No match found"
fi
This doesn't require a loop.
c.f. if a 2 strings BOTH present in 1 array in a bash script
$: x=bar
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
nope
$: x=hr
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
found
or
$: match=0
$: set -- foo bar
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
0
$: set -- foo crm
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
1
The logic seems incorrect.
If it does not match, you break the loop.
If it does match, the loop goes to the next iteration. Then it becomes unmatched and finally breaks the loop
declare -a arr=("crm" "hr" "pos")
not_match=0
for i in "${arr[#]}"; do
if [ "$2" = "$i" ]; then
not_match=1
break
fi
done
if [ $not_match -eq 0 ]; then
echo "No match found"
fi

Appending to an array in bash, why this isn't working?

I'm trying from a list of files with a pattern correctly matched by regex to check whether this value is in my array, if not, append it.
Unfortunately, this code that I build up inspired by some stack overflow post doesn't work (nothing is happened, the =~ doesn't seem to find the bash_rematch, and also it doesn't output anything?
sample_array=() #creating the array
for context_files in data/*.txt.gz # checking all the different samples id we have
do
[[ $context_files =~ SL[0-9]{6} ]]
echo 'context file:' "$context_files"
echo 'rematch:' "${BASH_REMATCH[0]}"
if ! [[ " ${sample_array[*]} " =~ (^|[[:space:]])"${BASH_REMATCH[0]}"($|[[:space:]]) ]]; then
echo 'condition matched'
echo 'rematch:' "${BASH_REMATCH[0]}"
sample_array+=(" ${BASH_REMATCH[0]} ")
fi
done
echo "${sample_array[*]}"
replacing this code by
sample_array=() #creating the array
for context_files in data/*.txt.gz # checking all the different samples id we have
do
[[ $context_files =~ SL[0-9]{6} ]]
echo 'context file:' "$context_files"
echo 'rematch:' "${BASH_REMATCH[0]}"
if ! [[ " ${sample_array[*]} " == "${BASH_REMATCH[0]}" ]]; then
echo 'condition matched'
echo 'rematch:' "${BASH_REMATCH[0]}"
sample_array+=(" ${BASH_REMATCH[0]} ")
fi
done
echo "${sample_array[*]}"
will this time add all the variable
output :
A B A B A B
I probably don't get something in how the if is managed and/or how the regex lookup in a bash array is to be made but I'd gladly get some help!
The second match is negated, so in order to enter the then part, the match needs to fail. A failed match resets $BASH_REMATCH.
#! /bin/bash
sample_array=()
for context_files in data/SL{111111,222222,333333,111111,222222}.txt.gz ; do
[[ $context_files =~ SL[0-9]{6} ]]
match=${BASH_REMATCH[0]}
echo 'context file:' "$context_files"
echo 'rematch:' "$match"
if ! [[ " ${sample_array[*]} " =~ (^|[[:space:]])"$match"($|[[:space:]]) ]]; then
echo 'condition matched'
echo 'rematch:' "$match"
sample_array+=(" $match ")
fi
done
echo "${sample_array[*]}"
Here is a completely alternative solution in bash-style like John Kugelman suggested:
printf %s\\n data/*.txt.gz | grep -Eo 'SL[0-9]{6}' | sort -u
If you need the results in an array, use mapfile:
mapfile -t array <(printf %s\\n data/*.txt.gz | grep -Eo 'SL[0-9]{6}' | sort -u)

Bash, compare string to arrayvalue

i try to match a parameter with some array content. At the if clauses should be true, but it wont be.
At the output before compare i got this:
VAL: drei_01 AND: drei
#!/bin/bash
array=( null_01 eins_01 zwei_01 drei_01 vier_01 )
lookarr() {
maxc=${#array[#]}
mbool=0
for((i=0; i<$maxc; i++))
do
val=${array[$i]}
echo "VAL: $val AND: $1"
if [[ $1 == *" $val "* ]]; then
echo "TESTENTRY1"
#do something
mbool=1
break
fi
done
if [[ $mbool -eq 0 ]]; then
echo "TESTENTRY2"
#do something else
fi
}
lookarr drei
thanks
Your if statement isn't matching because it is back-to-front and has extra spaces. For drei to match drei_01 you can replace your if statement with:
if [[ "$val" == *"$1"* ]]; then

Bash, how to iterate through an array once & append to end of line?

I am tasked with writing a script that analyzes code and attaches a comment with #Loopn or #Selection n that corresponds with the correct statements.
echo "enter full file name: "
read file
getArray(){
arr=()
while IFS= read -r line
do
arr+=("$line")
done < "$1"
}
getArray $file
echo "What file looks like before editing"
printf "%s\n" "${arr[#]}" #Test function to see if array works (it does)
#Declare variables
x=1
y=1
#Start main loop
for (( i=0; i<${#arr[#]}; i++ ));
do
if [[ "${arr[$i]}" == "while" ]] || [[ "${arr[$i]}" == "until" ]]
then sed -i 's/$/ #Loop'$x'/' $file && let "x++"
continue
elif [[ "${arr[$i]}" == "for" ]]
then sed -i 's/$/ #Loop'$x'/' $file && let "x++"
continue
elif [[ "${arr[$i]}" == "break" ]] || [[ "${arr[$i]}" == "done" ]]
then sed -i 's/$/ #Loop'$x'/' $file && let "x--"
continue
elif [[ "${arr[$i]}" == "if" ]] || [[ "${arr[$i]}" == "case" ]]
then sed -i 's/$/ #Selection'$y'/' $file && let "y++"
continue
elif [[ "${arr[$i]}" == "fi" ]] || [[ "${arr[$i]}" == "esac" ]]
then sed -i 's/$/ #Selection'$y'/' $file && let "y--"
continue
else
continue
fi
done < $file
Obviously I'm a newbie in bash, and my loop logic/language usage might be a bit wonky. Can anyone help? Right now the output makes it seem like I am iterating through the array more than once and Sed appends additional text per line.
In case it wasn't clear: each array element is a line of strings; if an array element contains while || for || until then it adds a #loop n and with each of the corresponding break or done, it adds the same #loop n. And likewise for if and case and fi esac except it adds #selection n.
Sample Input:
Before
Final=$(date -d "2016-12-15 14:00" "+%j")
while true ; do
Today=$(date "+%j")
Days=$((Final - Today))
if (( Days >= 14 )) ; then
echo party
elif (( Days >= 2 )) ; then
echo study
elif (( Days == 1 )) ; then
for Count in 1 2 3
do
echo panic
done
else
break
fi
sleep 8h
done
Expected Output:
After
Final=$(date -d "2016-12-15 14:00" "+%j")
while true ; do # loop 1
Today=$(date "+%j")
Days=$((Final - Today))
if (( Days >= 14 )) ; then # selection 1
echo party
elif (( Days >= 2 )) ; then
echo study
elif (( Days == 1 )) ; then
for Count in 1 2 3 # loop 2
do
echo panic
done # loop 2
else
break
fi # selection 1
sleep 8h
done # loop 1
Right now the output makes it seem like I am iterating through the array more than once and Sed appends additional text per line.
This is because the comment to be attached to one line is appended to each line of the file, since no line number is specified for the sed substitute commands in your script. There surely are more efficient solutions, but prepending the corresponding line number is sufficient.
Though your script is quite close to working, two more problems have to be addressed. One is that the == expressions you use to test for the keywords match only if the whole line contains nothing else than the keyword (not even leading space); to allow for indentation, =~ with an appropriate regular expression is useful. The other problem is the counting of the nesting depth (including the simple, but special case of break, where the depth remains unchanged); this seems more easy if we start at depth 0. So, your main loop could be:
x=0
y=0
#Start main loop
for (( i=0; i<${#arr[#]}; i++ ))
do let l=1+i # line numbers start at 1
if [[ "${arr[$i]}" =~ ^[$IFS]*(while|until|for) ]]
then sed -i $l"s/$/ #Loop$((++x))/" $file
elif [[ "${arr[$i]}" =~ ^[$IFS]*break ]]
then sed -i $l"s/$/ #Loop$x/" $file # no x-- here!
elif [[ "${arr[$i]}" =~ ^[$IFS]*done ]]
then sed -i $l"s/$/ #Loop$((x--))/" $file
elif [[ "${arr[$i]}" =~ ^[$IFS]*(if|case) ]]
then sed -i $l"s/$/ #Selection$((++y))/" $file
elif [[ "${arr[$i]}" =~ ^[$IFS]*(fi|esac) ]]
then sed -i $l"s/$/ #Selection$((y--))/" $file
fi
done <$file
You can test this;
echo "enter full file name: "
read file
getArray(){
arr=()
while IFS= read -r line
do
arr+=("$line")
done < "$1"
}
getArray $file
echo "What file looks like before editing"
printf "%s\n" "${arr[#]}" #Test function to see if array works (it does)
#Declare variables
x=1
y=1
#Start main loop
for (( i=0; i<${#arr[#]}; i++ ));
do
#echo $i "====" ${arr[$i]}
#echo "-------"
#echo "x"$x
if [[ "${arr[$i]}" == "while"* ]] || [[ "${arr[$i]}" == "until" ]]
then
sed -i "$((i+1))s/$/ #Loop $x/" $file
let "x++"
continue;
fi
if [[ "${arr[$i]}" == *"for"* ]]
then sed -i "$((i+1))s/$/ #Loop'$x'/" $file && let "x++"
continue
fi
if [[ "${arr[$i]}" == *"done"* ]]
then sed -i "$((i+1))s/$/ #Loop'$((x-1))'/" $file
continue
fi
if [[ "${arr[$i]}" == *"if"* ]] || [[ "${arr[$i]}" == *"case"* ]]
then
if [[ "${arr[$i]}" != *"elif"* ]]
then
sed -i "$((i+1))s/$/ #Selection'$y'/" $file && let "y++"
fi
continue
fi
if [[ "${arr[$i]}" == *"fi"* ]] || [[ "${arr[$i]}" == *"esac"* ]]
then sed -i "$((i+1))s/$/ #Selection'$((y-1))'/" $file
continue
fi
done < $file

Using mapfile to save output to associative arrays

In practicing bash, I tried writing a script that searches the home directory for duplicate files in the home directory and deletes them. Here's what my script looks like now.
#!/bin/bash
# create-list: create a list of regular files in a directory
declare -A arr1 sumray origray
if [[ -d "$HOME/$1" && -n "$1" ]]; then
echo "$1 is a directory"
else
echo "Usage: create-list Directory | options" >&2
exit 1
fi
for i in $HOME/$1/*; do
[[ -f $i ]] || continue
arr1[$i]="$i"
done
for i in "${arr1[#]}"; do
Name=$(sed 's/[][?*]/\\&/g' <<< "$i")
dupe=$(find ~ -name "${Name##*/}" ! -wholename "$Name")
if [[ $(find ~ -name "${Name##*/}" ! -wholename "$Name") ]]; then
mapfile -t sumray["$i"] < <(find ~ -name "${Name##*/}" ! -wholename "$Name")
origray[$i]=$(md5sum "$i" | cut -c 1-32)
fi
done
for i in "${!sumray[#]}"; do
poten=$(md5sum "$i" | cut -c 1-32)
for i in "${!origray[#]}"; do
if [[ "$poten" = "${origray[$i]}" ]]; then
echo "${sumray[$i]} is a duplicate of $i"
fi
done
done
Originally, where mapfile -t sumray["$i"] < <(find ~ -name "${Name##*/}" ! -wholename "$Name") is now, my line was the following:
sumray["$i"]=$(find ~ -name "${Name##*/}" ! -wholename "$Name")
This saved the output of find to the array. But I had an issue. If a single file had multiple duplicates, then all locations found by find would be saved to a single value. I figured I could use the mapfile command to fix this, but now it's not saving anything to my array at all. Does it have to do with the fact that I'm using an associative array? Or did I just mess up elsewhere?
I'm not sure if I'm allowed to answer my own question, but I figured that I should post how I solved my problem.
As it turns out, the mapfile command does not work on associative arrays at all. So my fix was to save the output of find to a text file and then store that information in an indexed array. I tested this a few times and I haven't seemed to encounter any errors yet.
Here's my finished script.
#!/bin/bash
# create-list: create a list of regular files in a directory
declare -A arr1 origray
declare indexray
#Verify that Parameter is a directory.
if [[ -d "$HOME/$1/" && -n "$1" ]]; then
echo "Searching for duplicates of files in $1"
else
echo "Usage: create-list Directory | options" >&2
exit 1
fi
#create list of files in specified directory
for i in $HOME/${1%/}/*; do
[[ -f $i ]] || continue
arr1[$i]="$i"
done
#search for all duplicate files in the home directory
#by name
#find checksum of files in specified directory
for i in "${arr1[#]}"; do
Name=$(sed 's/[][?*]/\\&/g' <<< "$i")
if [[ $(find ~ -name "${Name##*/}" ! -wholename "$Name") ]]; then
find ~ -name "${Name##*/}" ! -wholename "$Name" >> temp.txt
origray[$i]=$(md5sum "$i" | cut -c 1-32)
fi
done
#create list of duplicate file locations.
if [[ -f temp.txt ]]; then
mapfile -t indexray < temp.txt
else
echo "No duplicates were found."
exit 0
fi
#compare similarly named files by checksum and delete duplicates
count=0
for i in "${!indexray[#]}"; do
poten=$(md5sum "${indexray[$i]}" | cut -c 1-32)
for i in "${!origray[#]}"; do
if [[ "$poten" = "${origray[$i]}" ]]; then
echo "${indexray[$count]} is a duplicate of a file in $1."
fi
done
count=$((count+1))
done
rm temp.txt
This is kind of sloppy but it does what it's supposed to do. md5sum may not be the optimal way to check for file duplicates but it works. All I have to do is replace echo "${indexray[$count]} is a duplicate of a file in $1." with rm -i ${indexray[$count]} and it's good to go.
So my next question would have to be...why doesn't mapfile work with associative arrays?

Resources