Remove path prefix from unix tree

2019-07-19 04:38发布

问题:

I have a requirements in UNIX command where i have a path similar to this:

path/to/file/manyfiles.extensions

now i want the output something similar to-

file/manyfiles.extensions

now i can do this listing both /path/to and /path removing them one by one.

but i might get only "/path/to" as my input and i have to remove both from the tree in one command.

You might think that i can list 2 folders to remove, but in many a cases i get structures having 12-13 of subfolders in which i need only the first and last folder in many cases.

eg - `Source files - target/classes/

Remove prefix - target`

Result:

classes/my/code/HelloWorld.class
classes/my/code/HelloWorldImpl.class
classes/my/code/Main.class

回答1:

If I understand your question correctly, you want to strip off some given prefix on a path. Given a declaration path=/a/path/to/a/directory and a pattern *a/ you can strip off the prefix matching that pattern in two ways in Bash:

echo ${path#*a/}    # outputs "path/to/a/directory"
echo ${path##*a/}   # outputs "directory"

The first variant is non-greedy and stops at the first match, while the second is greedy and stops at the longest possible match for the pattern. The pattern *a/ occurs twice in out path here, so the results are then different.

In your case the pattern would be something like path/to and the path path/to/file/manyfiles.extensions, so one of the following would work, depending on whether you need to be greedy or not:

path=path/to/file/manyfiles.extensions
path=${path#*path/to/}   # assigns "file/manyfiles.extensions" to $path

or

path=path/to/file/manyfiles.extensions
path=${path##*path/to/}  # assigns "file/manyfiles.extensions" to $path

For reference, read about Bash parameter expansion.



回答2:

You'll want to the basename command to strip the directories off your filename, combined with the dirname command which will give you just the directory structure.

You can then use whatever logic you require to get the parts of the directory you need to keep, and put it back together with the filename.

Something like in this pseudocode:

original_file='/some/long/path/to/a/file'
file_name=$(basename $original_file)
dir_name=$(dirname $original_file)

short_dir='...use regex or whatever logic you need to pear ${dir_name} down (we'll need more info to help with this)'

new_file="${short_dir}/${file_name}"


回答3:

You can try grep -oP:

p="older2/folder3/folder4/folder5/folder6/folder7/file.txt"
s=$(grep -oP '([^/]+/){2}[^/]+/?$' <<< "$p")
echo "$s"

OUTPUT:

folder6/folder7/file.txt