2017-03-13 70 views
2

我想寫一個漂亮的打印機的LDAP條目,它只獲取一次根LDAP記錄,然後管輸出到tee調用每個部分漂亮的打印機。與進程替換誤解的三通

爲了便於說明,例如我的group_entry函數返回特定LDAP DN的LDIF。其中的細節並不重要,所以我們說,它總是返回:

dn: cn=foo,dc=example,dc=com 
cn: foo 
owner: uid=foo,dc=example,dc=com 
owner: uid=bar,dc=example,dc=com 
member: uid=foo,dc=example,dc=com 
member: uid=baz,dc=example,dc=com 
member: uid=quux,dc=example,dc=com 
custom: abc123 

我可以很容易地提取業主和成員分別與一點點grep「荷蘭國際集團和cut」荷蘭國際集團。然後,我可以將這些輔助DN傳遞到另一個LDAP搜索查詢中以獲取其真實姓名。了一個例子,假設我有一個pretty_print功能,即在LDAP parametrised屬性名稱,這確實是我剛纔提到的所有的,然後很好地格式化的一切與AWK:

$ group_entry | pretty_print owner 
Owners: 
foo Mr Foo 
bar Dr Bar 

$ group_entry | pretty_print member 
Members: 
foo Mr Foo 
baz Bazzy McBazFace 
quux The Artist Formerly Known as Quux 

這些做工精細獨立,但當我嘗試tee在一起,什麼都不會發生:

$ group_entry | tee >(pretty_print owner) | pretty_print member 
Members: 
[Sits there waiting for Ctrl+C] 

很顯然,我對此是如何工作的一些誤解,但我想不起來了。我究竟做錯了什麼?


編輯爲了完整起見,這裏是我完整的腳本:

#!/usr/bin/env bash 

set -eu -o pipefail 

LDAPSEARCH="ldapsearch -xLLL" 

group_entry() { 
    local group="$1" 
    ${LDAPSEARCH} "(&(objectClass=posixGroup)(cn=${group}))" 
} 

get_attribute() { 
    local attr="$1" 
    grep "${attr}:" | cut -d" " -f2 
} 

get_names() { 
    # We strip blank lines out of the LDIF entry, then we always have "dn" 
    # followed by "cn" records; we strip off the attribute name and 
    # concatenate those lines, then sort. So we get a sorted list of: 
    # {{distinguished_name}} {{real_name}} 
    xargs -n1 -J% ${LDAPSEARCH} -s base -b % cn \ 
    | grep -v "^$" \ 
    | cut -d" " -f2- \ 
    | paste - - \ 
    | sort 
} 

pretty_print() { 
    local attr="$1" 
    local -A pretty=([member]="Members" [owner]="Owners") 

    get_attribute "${attr}" \ 
    | get_names \ 
    | gawk -F'\t' -v title="${pretty[${attr}]}:" ' 
    BEGIN { print title } 
    { print "-", gensub(/^uid=([^,]+),.*$/, "\\1", "g", $1), "\t", $2 } 
    ' 
} 

# FIXME I don't know why tee with process substitution doesn't work here 
group_entry "$1" | pretty_print owner 
group_entry "$1" | pretty_print member 
+0

如果您可以分享您正在嘗試的實際代碼+您的錯誤輸出+您的預期輸出,我們將很容易爲您提供更好的幫助。 – Inian

+2

另一個問題是,對'pretty_print owner'的調用繼承了與tee相同的地方的標準輸出,這意味着'pretty_print member'會得到一些額外的意外輸入。 – chepner

+2

還有兩個調用'pretty_print'異步運行的問題,所以如果它們正在寫入同一個文件,它們的輸出可能會交錯。 – chepner

回答

1

您所描述的行爲看上去很像是可以在C程序,叉子和執行另一個程序出現的情況(因爲shell和xargs都是這樣做的),沒有正確處理所有打開的文件描述符。您可能處於這樣一種情況,即進程p1未終止,因爲它正在等待在其標準輸入上觀察EOF,但它永遠不會執行,因爲另一個進程爲管道的寫入端保存了打開的文件描述符它提供了p1的標準輸入,並且p2本身正在等待p1終止或執行一些其他動作。

不過,我看不出有什麼內在的錯誤在這方面您的管道,我不重現這個簡單的模型掛......

echo "foo" | tee >(cat) | cat 

...在4.2.46版本bash。這可能是因爲bash(即使它是同一個版本)或xargs版本中存在相關錯誤,但這是推測性的。我不認爲你的管道應該像你說的那樣掛,但我不準備開始指責。

無論如何,即使你的管道沒有掛起,它也沒有你想要的語義,就像@chepner在評論中指出的那樣。 pretty_print member將在其標準輸入上接收到tee的輸出,並且將包括兩個的輸出group_entry和輸出pretty_print owner。你可以考慮不同的實現它:因爲發球可以複用輸入兩個以上的方式,你可以通過這樣做一舉兩得:

group_entry "$1" | tee >(pretty_print owner) >(pretty_print member) 

但是,這也開啓了可能性,這兩個pretty_print執行的輸出將被混合,並且還響應group_entry輸出。您可以設想過濾掉group_entry輸出,但爲了避免混合,您需要確保兩個命令按順序運行。這爲基於tee的方法帶來了問題,因爲如果有任何tee的輸出阻塞,則整個管道可能會停滯。

一種解決方案是將一個或兩個pretty_print命令的輸出重定向到文件。或者,如果兩個輸出都必須輸出到標準輸出,那麼我看不到任何好的選擇,只能捕獲group_entry輸出,並將其分別輸入到每個pretty_print作業中。你可以將它捕獲到一個文件中,但這是不必要的,並且有點麻煩。考慮這個代替:

entry_lines=$(group_entry "$1") 
pretty_print owner <<<"$entry_lines" 
pretty_print member <<<"$entry_lines" 

使用命令替換捕獲的group_entry在殼變量(包括新行)的輸出,並且採用了在這裏字符串它重播到每個pretty_print過程。