5

I am searching for one file, say "file1.txt", and output of find command is like below.

/home/nicool/Desktop/file1.txt
/home/nicool/Desktop/dir1/file1.txt
/home/nicool/Desktop/dir1/dir2/file1.txt

In above cases I want only common parent directory, which is "/home/nicool/Desktop" in above case. How it can be achieved using bash? Please help to find general solution for such problem.

Kijewski
  • 25,517
  • 12
  • 101
  • 143
Nicool
  • 259
  • 1
  • 7

3 Answers3

2

This script reads lines and stores the common prefix in each iteration:

# read a line into the variable "prefix", split at slashes
IFS=/ read -a prefix

# while there are more lines, one after another read them into "next",
# also split at slashes
while IFS=/ read -a next; do
    new_prefix=()

    # for all indexes in prefix
    for ((i=0; i < "${#prefix[@]}"; ++i)); do
        # if the word in the new line matches the old one
        if [[ "${prefix[i]}" == "${next[i]}" ]]; then
            # then append to the new prefix
            new_prefix+=("${prefix[i]}")
        else
            # otherwise break out of the loop
            break
        fi
    done

    prefix=("${new_prefix[@]}")
done

# join an array
function join {
    # copied from: http://stackoverflow.com/a/17841619/416224
    local IFS="$1"
    shift
    echo "$*"
}

# join the common prefix array using slashes
join / "${prefix[@]}"

Example:

$ ./x.sh <<eof
/home/nicool/Desktop1/file1.txt
/home/nicool/Desktop2/dir1/file1.txt
/home/nicool/Desktop3/dir1/dir2/file1.txt
eof
/home/nicool
Kijewski
  • 25,517
  • 12
  • 101
  • 143
1
lcp() {
    local prefix path
    read prefix

    while read path; do
        while ! [[ $path =~ ^"$prefix" ]]; do
            [[ $prefix == $(dirname "$prefix") ]] && return 1
            prefix=$(dirname "$prefix")
        done
    done

    printf '%s\n' "$prefix"
    return 0
}

This finds the longest common prefix of all of the lines of standard input.

$ find / -name file1.txt | lcp
/home/nicool/Desktop
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
1

I don't think there's a bash builtin for this, but you can use this script, and pipe your find into it.

read -r FIRSTLINE
DIR=$(dirname "$FIRSTLINE")

while read -r NEXTLINE; do
  until [[ "${NEXTLINE:0:${#DIR}}" = "$DIR" || "$DIR" = "/" ]]; do
    DIR=$(dirname "$DIR")
  done
done

echo $DIR

For added safety, use -print0 on your find, and adjust your read statements to have -d '\0'. This will work with filenames that have newlines.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251