LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 01-02-2013, 12:43 PM   #1
eamesj
Member
 
Registered: May 2006
Posts: 54

Rep: Reputation: 1
bash select items not in array


Hi all,

I'm building an array in bash and i would like to perform actions on everything that is NOT in the array

#! /bin/bash
while read -r input ; do
[[ $input == quit ]] && break
array+= ( "$input" )
done
for index (NOT)in ${!array[@]}; do
do something
done

anyone know how i can do this?
thanks,
 
Old 01-02-2013, 03:40 PM   #2
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Maybe a stupid question, but what are the items/indices that are "not in array"?
 
Old 01-02-2013, 11:26 PM   #3
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Firstly, please use [code][/code] tags around your code.

The way you have constructed the array it will have no holes in it so simply use the size of the array and all numbers after that, which will be an infinite for loop.
 
Old 01-03-2013, 10:28 AM   #4
eamesj
Member
 
Registered: May 2006
Posts: 54

Original Poster
Rep: Reputation: 1
Sorry, I suppose it wasn't clear enough.

normal coding... create array and do something to every item in the list...

Code:
#! /bin/bash
echo "enter items into list to process, use new line for multiple items"
# create array of items. quit exits list builder
while read -r input ; do
   [[ $input == quit ]] && break 
   array+= ( "$input" )
done
# do something to all items in array
for index in ${!array[@]}; do
   do something 
done

- i would like to be able to do something in loop for items not in the array. for example a list of directories, I would like the user to make a list of items (directories) to exclude and perform an action on all directories except for the ones in the list.

so id like something like...

Code:
#! /bin/bash
echo "enter items into list to NOT process, use new line for multiple items"
# create array of items. quit exits list builder
while read -r input ; do
   [[ $input == quit ]] && break 
   array+= ( "$input" )
done
# do something to all items not in array
for index notin ${!array[@]}; do
   do something 
done
I'm not sure that an array like this is the best way to go about this so if anyone has any suggestions id appreciate it.

Hope that's a bit clearer


Grail, afraid i'm really not sure what you mean by having 'holes' in an array can you clarify your "use the size of the array and all numbers after that, which will be an infinite for loop." ?

thanks,
 
Old 01-03-2013, 11:43 AM   #5
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
The problem is, you can only iterate over something that is in a list. To do something with everything not in a list is like if I told you to name all things in the universe with the exception of say, my car, your dog and a pencil on grail's table. The question only makes sense if you have two lists - then you may for example want to do something for all items in list1 except for those that are also in list2.
So, for example to do something with all files in a directory that are not in list you may reason as follows:

Code:
for fname in *; do 
    if (fname is not in list); then
        do_something;
    fi
done;

Last edited by millgates; 01-03-2013 at 11:57 AM.
 
Old 01-03-2013, 11:49 AM   #6
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Quote:
Grail, afraid i'm really not sure what you mean by having 'holes' in an array can you clarify your "use the size of the array and all numbers after that, which will be an infinite for loop." ?
Part of your code says:
Code:
for index notin ${!array[@]}; do
By asking for indexes not in the array you either create an array with indexes missing, ie 0,1,3,6,7,9
This would then mean you have "holes" at positions - 2,4,5,8 and nothing in any index after 9

However, you create your array using the following:
Code:
array+= ( "$input" )
So unless this is an existing array, you are not adding to one that already has "holes" hence there are none and all up until the last index will have data associated with it.
This then implies that the size of the array, found using:
Code:
${#array[*]}
would give you the size of the array which would also be the point from which the last index would have no data and so it and all after it (to infinity)
would be the numbers you are looking for (although I would guess this is not the case)

So your current example seems to be the same as the original so I am still at a loss as to what it is you actually wish to do?

On reading your question, it sounds like you may need to arrays / lists and whatever is not in one of them needs to be dealt with???
 
Old 01-03-2013, 12:13 PM   #7
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
One way to do this is adopt the convention that any entry which is prefixed with a dash (minus sign) should not be processed.

Try this ...
Code:
# Fill the array named "list" with user inputs.
echo "Enter list items, one per line, then enter <null> to escape..."  
    echo "Prefix any list item  with dash (-) to signify 'no processing'." 
for (( j=1; j<9999; j++ ))
  do
    # Solicit user response
    read reply
    if [ -z "$reply" ]   # Test for null entry
        then break
    fi
    list[$j]=$reply  # Save the user response
  done
 
# Regurgitate the list contents.
echo; echo "These are your orders..."
for (( j=1; j<=${#list[@]}; j++ ))
  do
    if [ '-' == ${list[$j]:0:1} ]   # Test first character
       then echo "DON'T process "${list[$j]:1}
       else echo "DO process " ${list[$j]}
    fi
  done
My test results ...
Code:
Enter list items, one per line, then enter <null> to escape...
Prefix any list item  with dash (-) to signify 'no processing'.
able
-baker
charlie


These are your orders...
DO process  able
DON'T process baker
DO process  charlie
Daniel B. Martin
 
Old 01-03-2013, 01:18 PM   #8
eamesj
Member
 
Registered: May 2006
Posts: 54

Original Poster
Rep: Reputation: 1
ok, so if i understand correctly. Going with the directory example (perform an action on all directories except those in a list specified by the user)

I would need to create an array of all of the sub-directories within a given directory

e.g.
/home/users/user1
/home/users/user2
/home/users/user3
/home/users/user4

have the script ask for directories not to be processed and create an array out of it:

Code:
#! /bin/bash
# create an array of all directories..
declare -a dirs
i=1
for d in /home/users/*
do
   dirs[i++]="${d%/}"
done

echo "enter items into list to process, use new line for multiple items"
# create array of items. quit exits list builder
while read -r input ; do
   [[ $input == quit ]] && break 
   array+= ( "$input" )
done
and then compare one array with the other ?

How can i compare the arrays? or there a better method of doing this?
 
Old 01-03-2013, 02:13 PM   #9
eamesj
Member
 
Registered: May 2006
Posts: 54

Original Poster
Rep: Reputation: 1
solved :)

that'll work like a charm, thanks danielbmartin... very tidy
 
Old 01-03-2013, 02:53 PM   #10
eamesj
Member
 
Registered: May 2006
Posts: 54

Original Poster
Rep: Reputation: 1
I jumped the gun a little in making this solved...

although that solution works fune, its a bit of a pain for the user to enter all of the directories if there are a lot. Right now I've got two arrays, one for the complete set of directories (dirs) and one listbuilder (list). Is there a way to compare one with the other so that if an entry in (dirs) does not exist in (list) then add a '-' and therefore not process?

Code:
declare -a dirs
i=1
for directory in /home/users/*/
do
    dirs[i++]="${directory}"
done
echo "There are ${#dirs[@]} dirs in the current path"
for((i=1;i<=${#dirs[@]};i++)); do
    echo "${dirs[i]}"
done

echo -e "\n\n"

# Fill the array named "list" with user inputs.
echo "Enter list items, one per line, then press enter <null> to escape..."  
    echo "Prefix any list item  with dash (-) to signify 'no processing':" 
for (( i=1; i<=${#dirs[@]}; i++ )); do

# Solicit user response
    read reply
    if [ -z "$reply" ]   # Test for null entry
        then break
    fi
    list[$i]=$reply  # Save the user response
  done

# Regurgitate the list contents.
echo; echo "These are your orders..."
for (( i=1; i<=${#list[@]}; i++ )); do
    if [ '-' == ${list[$i]:0:1} ]   # Test first character
    then echo "DON'T process "${list[$i]:1}
    else echo "DO process " ${list[$i]}
    fi
done
 
Old 01-03-2013, 02:58 PM   #11
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
Quote:
Originally Posted by eamesj View Post
Right now I've got two arrays, one for the complete set of directories (dirs) and one listbuilder (list). Is there a way to compare one with the other so that if an entry in (dirs) does not exist in (list) then add a '-' and therefore not process?
Perhaps you are seeking the comm command.
Code:
daniel@daniel-desktop:~$ comm --help
Usage: comm [OPTION]... FILE1 FILE2
Compare sorted files FILE1 and FILE2 line by line.

With no options, produce three-column output.  Column one contains
lines unique to FILE1, column two contains lines unique to FILE2,
and column three contains lines common to both files.

  -1              suppress lines unique to FILE1
  -2              suppress lines unique to FILE2
  -3              suppress lines that appear in both files

  --check-order     check that the input is correctly sorted, even
                      if all input lines are pairable
  --nocheck-order   do not check that the input is correctly sorted
  --output-delimiter=STR  separate columns with STR
      --help     display this help and exit
      --version  output version information and exit

Note, comparisons honor the rules specified by `LC_COLLATE'.

Daniel B. Martin
 
Old 01-03-2013, 03:22 PM   #12
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Quote:
Originally Posted by danielbmartin View Post
One way to do this is adopt the convention that any entry which is prefixed with a dash (minus sign) should not be processed.
This is quite nice but I fail to see how this solves the issue. You still have to manually mark all items you don't want to process. Why would you even write those entries to stdin?

Code:
declare -a dirs
i=1
for directory in /home/users/*/
do
    dirs[i++]="${directory}"
done
the same can be done as

Code:
dirs=(/home/users/*/)
Do you need those arrays stored anyway?
Also, maybe you should tell us what are you trying to achieve in the first place. Then we can recommend you a better tool for the job. What are the two lists you have? Are they in files? What do you want to do with all of them?

another example:

Code:
#!/bin/bash

declare -A exclude;

echo "enter filenames to exclude from processing, one per line, end with Ctrl+D:"

while read -r line; do
    exclude["$line"]=" "
done

for fname in *; do
    [[ -z ${exclude["$fname"]} ]] && do_something_with_file
done

Last edited by millgates; 01-03-2013 at 03:24 PM.
 
Old 01-03-2013, 05:11 PM   #13
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
Perhaps you will find this snippet useful.

InFile1 ...
Code:
alan.txt
barney.txt
charlie.txt
david.txt
edgar.txt
frank.txt
InFile2 ...
Code:
alan.txt
charlie.txt
frank.txt
Code ...
Code:
# Display contents of InFile1.
echo; echo "InFile1 ..."; cat $InFile1

# Display contents of InFile2.
echo; echo "InFile2 ..."; cat $InFile2

echo
echo "Identify files which exist in InFile1 and *not* in InFile2,"
echo " and prefix them with a dash."
comm -2 $InFile1 $InFile2 |sed 's/^/-/; s/-\t//'
My test results ...
Code:
InFile1 ...
alan.txt
barney.txt
charlie.txt
david.txt
edgar.txt
frank.txt

InFile2 ...
alan.txt
charlie.txt
frank.txt

Identify files which exist in InFile1 and *not* in InFile2,
 and prefix them with a dash.
alan.txt
-barney.txt
charlie.txt
-david.txt
-edgar.txt
frank.txt

Daniel B. Martin

Last edited by danielbmartin; 01-03-2013 at 10:05 PM. Reason: Simplify the code
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
bash to loop thorouh mysql select array z01krh Programming 3 12-27-2017 08:30 PM
[SOLVED] reference bash array values, using a variable with a value of the array name gusthecat Programming 5 03-07-2012 03:41 PM
Korn - combine select with an array lucmove Programming 3 06-09-2010 07:29 AM
[bash] indirect array reference to array with values containing spaces Meson Linux - Software 9 06-04-2010 09:38 PM
bash: use file as input into array, parse out other variables from array using awk beeblequix Linux - General 2 11-20-2009 10:07 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 10:36 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration