使用sed或awk从绝对路径中提取目录名称

时间:2011-12-19 10:57:07

标签: bash awk sed

我想分割这一行

/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh

/home/edwprod/abortive_visit/bin

使用sedawk脚本?你可以帮忙吗?

7 个答案:

答案 0 :(得分:14)

目录名

kent$  dirname "/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh"
/home/edwprod/abortive_visit/bin

SED

kent$  echo "/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh"|sed 's#/[^/]*$##'
/home/edwprod/abortive_visit/bin

的grep

kent$  echo "/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh"|grep  -oP '^/.*(?=/)'
/home/edwprod/abortive_visit/bin

AWK

kent$  echo "/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh"|awk -F'/[^/]*$' '{print $1}'
/home/edwprod/abortive_visit/bin

答案 1 :(得分:5)

可能是命令 dirname 是您要搜索的内容吗?

dirname /home/edwprod/abortive_visit/bin/abortive_proc_call.ksh

或者如果您想要sed,请参阅我的解决方案:

echo /home/edwprod/abortive_visit/bin/abortive_proc_call.ksh | sed 's/\(.*\)\/.*/\1/'

答案 2 :(得分:2)

awk + ​​for:

echo "/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh" | awk 'BEGIN{res=""; FS="/";}{ for(i=2;i<=NF-1;i++) res=(res"/"$i);} END{print res}'

答案 3 :(得分:1)

我猜这段带 awk 的代码与 dirname 完全相同。

它非常简单,工作成本非常低。祝你好运。

代码

$ foo=/app/java/jdk1.7.0_71/bin/java
$ echo "$foo" | awk -F "/*[^/]*/*$" '
{ print ($1 == "" ? (substr($0, 1, 1) == "/" ? "/" : ".") : $1); }'

结果

/app/java/jdk1.7.0_71/bin

测试

  • foo=/app/java/jdk1.7.0_71/bin/java - &gt; /app/java/jdk1.7.0_71/bin
  • foo=/app/java/jdk1.7.0_71/bin/ - &gt; /app/java/jdk1.7.0_71
  • foo=/app/java/jdk1.7.0_71/bin - &gt; /app/java/jdk1.7.0_71
  • foo=/app/ - &gt; /
  • foo=/app - &gt; /
  • foo=fighters/ - &gt; .

更多

如果您没有这样的awk分隔符,请尝试这种方式。

$ echo $foo | awk '{
 dirname = gensub("/*[^/]*/*$", "", "", $0);
 print (dirname == "" ? (substr($0, 1, 1) == "/" ? "/" : ".") : dirname);
 }'

答案 4 :(得分:0)

对于现在可用的大多数平台和Unix / Linux shell dirname

dirname /home/edwprod/abortive_visit/bin/abortive_proc_call.ksh

使用dirname是最简单的方法,但不建议用于跨平台脚本,例如在autoconf文档http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Limitations-of-Usual-Tools.html#Limitations-of-Usual-Tools的最新版本中。

所以我的基于sed的完整功能版本替代dirname

str="/home/edwprod/abortive_visit/bin/abortive_proc_call.ksh"
echo "$str" | sed -n -e '1p' | sed  -e 's#//*#/#g' -e 's#\(.\)/$#\1#' -e 's#^[^/]*$#.#' -e 's#\(.\)/[^/]*$#\1#' -

<强>示例:

它的作用类似于dirname

  • 对于/aa/bb/cc这样的路径,它会打印/aa/bb
  • 对于/aa/bb这样的路径,它会打印/aa
  • 对于/aa/bb/之类的路径,它也会打印/aa
  • 对于/aa/这样的路径,它会打印/aa
  • 对于/这样的路径,它会打印/
  • 对于aa这样的路径,它会打印.
  • 对于aa/这样的路径,它会打印.

即:

  • 跟踪/
  • 的方式是正确的
  • 对于仅包含aaaa/
  • 等基本名称的路径,它可以正常工作
  • 对于以/开头的路径和路径/本身。
  • ,它的工作正确
  • 如果$str最后包含\n,那么它是正确的,即使有很多\n
  • 它使用跨平台sed命令
  • 它会将/// ///)的所有组合更改为/
  • 对于包含换行符和当前区域设置无效的字符的路径,它无法正常工作。

注意 basename的替代方案可能很有用:

echo "$str" | awk -F"/" '{print $NF}' -

答案 5 :(得分:0)

此外,除了肯特的答案,另一种awk解决方案是:

awk 'BEGIN{FS=OFS="/"}{NF--}1'

与肯特(Kent)所患的疾病相同。以下较长的Awk纠正了所有缺陷:

awk 'BEGIN{FS=OFS="/"}{gsub("/+","/")}
     {s=$0~/^\//;NF-=$NF?1:2;$0=$0?$0:(s?"/":".")};1' <file>

下表显示了区别:

| path       | dirname | awk full | awk short |
|------------+---------+----------+-----------|
| .          | .       | .        |           |
| /          | /       | /        |           |
| foo        | .       | .        |           |
| foo/       | .       | .        | foo       |
| foo/bar    | foo     | foo      | foo       |
| foo/bar/   | foo     | foo      | foo/bar   |
| /foo       | /       | /        |           |
| /foo/      | /       | /        | /foo      |
| /foo/bar   | /foo    | /foo     | /foo      |
| /foo/bar/  | /foo    | /foo     | /foo/bar  |
| /foo///bar | /foo    | /foo     | /foo//    |

注意: dirname是真正的解决方法,除非您必须处理存储在文件中的大量文件。

答案 6 :(得分:0)

我总是惊讶于一些单线球员的聪明程度。就我而言,我追求可读性,所以这是我的实现。这是一个普通的 Bourne shell 脚本,依赖于模仿 dirname 的 sed 和 grep。

strip_trailing_slashes() {
  printf "$1" | sed 's/[/]*$//'
}

# If empty arg, return .
# If nothing but slashes, return /
# If no slashes after stripping all trailing slashes, return .
# Otherwise, return everything up until last path component
dirname() {
  if [ -z "$1" ]; then printf '.'; return; fi
  if ! $(printf "$1" | grep -q '[^/]'); then printf '/'; return; fi

  local s="$(strip_trailing_slashes $1)"
  if ! $(printf "$s" | grep -q '/'); then printf '.'; fi

  printf "$(strip_trailing_slashes $(printf $s | sed 's/[^/]*$//'))"
}

这是一个 gist,其中包括针对以下情况的 44 项测试 -- 还针对 dirname 进行了兼容性测试。

Expect '.' for ('""' '.' '.foo' './foo' 'foo' 'foo/' 'foo///' '..' '..foo' '..foo//' '../')
Expect '/' for ('/' '//' '///')
Expect 'a/b' for ('a/b/c' 'a/b/c/' 'a/b/c///')
Expect '/a/b' for ('/a/b/c' '/a/b/c/' '/a/b/c///')
Expect '//a/b' for ('//a/b/c' '//a/b/c//' '/a/b/c')

几点:

  1. @Роман Коптев 的one-liner sed version 很棒(我怎么可能是唯一一个被点赞的人?)。
  2. 仅有的两次测试失败是由于 oneliner 版本,但与 dirname 不同。 oneliner 版本将下面的“//a/b/c”转换成“/a/b”,这可能不是你想要的; dirname 不会去除额外的前导斜杠并返回“//a/b”。我选择使我的版本与 dirname 兼容。 (没有一个版本在中间分隔路径组件中去除额外的斜线)。

向@Роман Коптев 致敬oneliner 版本!