Array objects with whitespace in names not conjoined - arrays

AVAILABLEDIR=("${AVAILABLEDIR[#]}" "$(ls $LOC -AFl | sed "1 d" | grep "/$" | awk '{ print $9,$10 }')")
I'm trying to create an array using this command, however when it adds objects to the array, it adds $9 and $10 seperately, is there a way to tell the array to have both of these arguments joined? This is what I want:
[Directory 1/] [Directory 2/] [Directory 3/]
instead of
[Directory] [1/] [Directory] [2/] [Directory] [3/]
Thank you for your help

Don't use a pipeline headed by ls for this; just use a glob.
pushd "$LOC"
AVAILABLEDIR+=( */ )
popd
pushd works like cd, but saves the current directory on a stack before changing. */ is a pattern that matches all directory names in the current directory; += appends the matching directories to the current value of AVAILABLEDIR. popd removes the directory name from the top of the stack and cds there. The pushd/popd combination is the easiest way to add Directory 1, rather than $LOC/Directory 1, to the array.

Related

Populate an array with list of directories existing in a given path in Bash

I have a directory path where there are multiple files and directories.
I want to use basic bash script to create an array containing only list of directories.
Suppose I have a directory path:
/my/directory/path/
$ls /my/directory/path/
a.txt dirX b.txt dirY dirZ
Now I want to populate array named arr[] with only directories, i.e. dirX, dirY and dirZ.
Got one post but its not that relevant with my requirement.
Any help will be appreciated!
Try this:
#!/bin/bash
arr=(/my/directory/path/*/) # This creates an array of the full paths to all subdirs
arr=("${arr[#]%/}") # This removes the trailing slash on each item
arr=("${arr[#]##*/}") # This removes the path prefix, leaving just the dir names
Unlike the ls-based answer, this will not get confused by directory names that contain spaces, wildcards, etc.
Try:
shopt -s nullglob # Globs that match nothing expand to nothing
shopt -s dotglob # Expanded globs include names that start with '.'
arr=()
for dir in /my/directory/path/*/ ; do
dir2=${dir%/} # Remove the trailing /
dir3=${dir2##*/} # Remove everything up to, and including, the last /
arr+=( "$dir3" )
done
Try:
baseDir="/my/directory/path/"
readarray -d '' arr < <(find "${baseDir}" -mindepth 1 -maxdepth 1 -type d -print0)
Here the find command outputs all directories within the baseDir, then the readarray command puts these into an array names arr.
You can then work over the array with:
for directory in "${arr[#]}"; do
echo "${directory}"
done
Note: This only works with bash version 4.4-alpha and above. (See this answer for more.)

Bash Array Script Exclude Duplicates

So I have written a bash script (named music.sh) for a Raspberry Pi to perform the following functions:
When executed, look into one single directory (Music folder) and select a random folder to look into. (Note: none of these folders here have subdirectories)
Once a folder within "Music" has been selected, then play all mp3 files IN ORDER until the last mp3 file has been reached
At this point, the script would go back to the folders in the "Music" directory and select another random folder
Then it would again play all mp3 files in that folder in order
Loop indefinitely until input from user
I have this code which does all of the above EXCEPT for the following items:
I would like to NOT play any other "album" that has been played before
Once all albums played once, then shutdown the system
Here is my code so far that is working (WITH duplicates allowed):
#!/bin/bash
folderarray=($(ls -d /home/alphekka/Music/*/))
for i in "${folderarray[#]}";
do
folderitems=(${folderarray[RANDOM % ${#folderarray[#]}]})
for j in "${folderitems[#]}";
do
echo `ls $j`
cvlc --play-and-exit "${j[#]}"
done
done
exit 0
Please note that there isn't a single folder or file that has a space in the name. If there is a space, then I face some issues with this code working.
Anyways, I'm getting close, but I'm not quite there with the entire functionality I'm looking for. Any help would be greatly appreciated! Thank you kindly! :)
Use an associative array as a set. Note that this will work for all valid folder and file names.
#!/bin/bash
declare -A folderarray
# Each folder name is a key mapped to an empty string
for d in /home/alphekka/Music/*/; do
folderarray["$d"]=
done
while [[ "${!folderarray[*]}" ]]; do
# Get a list of the remaining folder names
foldernames=( "${!folderarray[#]}" )
# Pick a folder at random
folder=${foldernames[RANDOM%${#foldernames[#]}]}
# Remove the folder from the set
# Must use single quotes; see below
unset folderarray['$folder']
for j in "$folder"/*; do
cvlc --play-and-exit "$j"
done
done
Dealing with keys that contain spaces (and possibly other special characters) is tricky. The quotes shown in the call to unset above are not syntactic quotes in the usual sense. They do not prevent $folder from being expanded, but they do appear to be used by unset itself to quote the resulting string.
Here's another solution: randomize the list of directories first, save the result in an array and then play (my script just prints) the files from each element of the array
MUSIC=/home/alphekka/Music
OLDIFS=$IFS
IFS=$'\n'
folderarray=($(ls -d $MUSIC/*/|while read line; do echo $RANDOM $line; done| sort -n | cut -f2- -d' '))
for folder in ${folderarray[*]};
do
printf "Folder: %s\n" $folder
fileArray=($(find $folder -type f))
for j in ${fileArray[#]};
do
printf "play %s\n" $j
done
done
For the random shuffling I used this answer.
One liner solution with mpv, rl (randomlines), xargs, find:
find /home/alphekka/Music/ -maxdepth 1 -type d -print0 | rl -d \0 | xargs -0 -l1 mpv

bash array count always returns 1

I searched all over for this, but the terms are apparently too general. I'm writing a script to search a group of folders for .mp3 files. Some folders don't have mp3's so they have to be excluded.
I created an array to hold the uniq'd folder names. This find command will get the folders I need.
Folders=$(sudo find /my/music/ -type f -name "*.mp3" | cut -d'/' -f7 | sort -u)
When I try to count the number of folders in the array, I always get 1
echo ${#Folders[#]}
echo ${Folders[#]} prints them out on separate lines so I thought they were separate array elements. Can anyone explain what is going on? You might have to jiggle the field number in the cut command to reproduce locally.
Folders is not an array but a variable.
You need:
Folders=( $(sudo find /my/music/ -type f -name "*.mp3" | cut -d'/' -f7 | sort -u) )
i.e. enclose the command substitution with (). Now ${#Folders[#]} would give you the number of elements of array Folders.
Or do :
sudo find /my/music/ -type f -name "*.mp3" | cut -d'/' -f7 | sort -u | wc -l
Note
wc -l prints the number of lines which in this case would be the number of unique files
to make things a bit more explicit, use -printf "%p\n" option with find where %p specifier prints the file with full path.
Assuming bash 4 or later, don't use find here; use the globstar operator.
shopt -s globstar
folders=( /my/music/**/*.mp3 )
Also assuming that cut -d/ -f7 is supposed to extract the filename alone, follow this up with
folders=${folders[#]##*/}
Other methods for populating the array must take more care to accomodate files containing whitespace or characters like ?, *, or [. File names containing newlines (rare, but not illegal) are much more difficult to handle correctly. Pathname expansion is done inside the shell, so you don't need to worry about any such special characters.

passing values of text file to array in shell scripting

My script fetches the names of directories in a path and stores in a text file.
#!/bin/bash
MYDIR="/bamboo/artifacts"
DIRS=`ls -d /bamboo/artifacts/* | cut -d'/' -f4 > plan_list.txt`
plan_list.txt:
**************
PLAN1
PLAN2
PLAN3
Now I am trying to pass each of these directory names to a URL to get output like this.
http://bamboo1.test.com:8080/browse/PLAN1
http://bamboo1.test.com:8080/browse/PLAN2
http://bamboo1.test.com:8080/browse/PLAN3
The script to do that doesn't seem to work
bambooServer="http://bamboo1.test.com:8080/browse/"
for DIR in $DIRS
do
echo `$bambooServer+$DIR`
done
Could someone please tell me what I am missing here? Instead of storing the ls command output to a plan_list.txt file i tried passing to array but that didn't work well too.
DIRS=`ls -d /bamboo/artifacts/* | cut -d'/' -f4 > plan_list.txt`
DIRS is just an empty variable since your command is not producing any output and just redirecting output to plan_list.txt.
You can rewrite your script like this:
#!/bin/bash
mydir="/bamboo/artifacts"
cd "$mydir"
bambooServer="http://bamboo1.test.com:8080/browse/"
for dir in */
do
echo "$bambooServer$dir"
done
*/ is the glob pattern to get all the directories in your current path.

Script for renameing special characters files and directories

I am looking for a script to rename files and directories that have special characters in them.
My files:
?rip?ev <- Directory
- Juhendid ?rip?evaks.doc <- Document
- ?rip?ev 2 <- Subdirectory
-- t?ts?.xml <- Subdirectory file
They need to be like this:
ripev <- Directory
- Juhendid ripevaks.doc <- Document
- ripev 2 <- Subdirectory
-- tts.xml <- Subdirectory file
I need to change the files and the folders so that the filetype stays the same as it is for example .doc and .xml wont be lost. Last time I did it with rename it lost every filetype and the files were moved to mother directory in this case ?rip?ev directory and subdirectories were empty. Everything was located under the mother directory /home/samba/.
So in this case I need just to rename the question mark in the file name and directory name, but not to move it anywhere else or lose any other character or the filetype. I have been looking around google for a answer but haven't found one. I know it can be done with find and rename, but haven't been able to over come the complexity of the script. Can anyone help me please?
You can just do something like this
find -name '*\?*' -exec bash -c 'echo mv -iv "$0" "${0//\?/}"' {} \;
Note the echo before the mv so you can see what it does before actually changing anything. Otherwise above:
searches for ? in the name (? is equivalent to a single char version of * so needs to be escaped)
executes a bash command passing the {} as the first argument (since there is no script name it's $0 instead of $1)
${0//\?/} performs parameter expansion in bash replacing all occurrences of ? with nothing.
Note also that file types do not depend on the name in linux, but this should not change any file extension unless they contain ?.
Also this will rename symlinks as well if they contain ? (not clear whether or not that was expected from question).
I usually do this kind of thing in Perl:
#!/usr/bin/perl
sub procdir {
chdir #_[0];
for (<*>) {
my $oldname = $_;
rename($oldname, $_) if s/\?//g;
procdir($_) if -d;
}
chdir "..";
}
procdir("top_directory");

Resources