Bir uygulamanın standart çıkışının bir boru değil, bir terminal olduğunu düşünmesi için nasıl kandırılır


152

" Stdin'in terminal mi yoksa boru mu olduğunu algıla? " Nın tersini yapmaya çalışıyorum .

STDOUT'ta bir boru algıladığı için çıktı biçimini değiştiren bir uygulama çalıştırıyorum ve yeniden yönlendirirken aynı çıktıyı alabilmem için bunun etkileşimli bir terminal olduğunu düşünmesini istiyorum.

Onu bir expectbetik içine sarmanın veya proc_open()PHP'de a kullanmanın işe yarayacağını düşünüyordum, ama olmuyor.

Herhangi bir fikir var mı?


8
Does empty.sf.net yardım?
ephemient

1
@ephemient: bir cevap olmalıydı. Bu arada harika kullanım ...
neuro

Soru stdout'tan bahsediyor, ancak başlık stdin'den bahsediyor. Başlığın yanlış olduğunu düşünüyorum.
Piotr Dobrogost

Yanıtlar:


177

Aha!

scriptKomut istediğimizi yapar ...

script --return --quiet -c "[executable string]" /dev/null

Hile yapar!

Usage:
 script [options] [file]

Make a typescript of a terminal session.

Options:
 -a, --append                  append the output
 -c, --command <command>       run command rather than interactive shell
 -e, --return                  return exit code of the child process
 -f, --flush                   run flush after each write
     --force                   use output file even when it is a link
 -q, --quiet                   be quiet
 -t[<file>], --timing[=<file>] output timing data to stderr or to FILE
 -h, --help                    display this help
 -V, --version                 display version

1
+1: Statik ilklendirme yapan bir kitaplıkla soruna takılmanız yeterli. Fedora 12'de son zamanlarda yapılan bir değişiklik, bu başlatmanın, exe lanched bir tty içinde olmadığında başarısız olmasına neden oldu. Numaranız mükemmel çalışıyor. Komut dosyası varsayılan olarak yüklendiğinden unbuffer'ı tercih ettim!
neuro

9
Eğer onu less -R, terminal girişinin nereye gittiği gibi etkileşimli bir şeye dönüştürmek istiyorsanız less -R, o zaman fazladan hileye ihtiyacınız var. Örneğin, renkli bir versiyonunu istedim git status | less. Sen geçmesi gerekiyor -Ro renkleri saygı sırayla daha az ve kullanmak gerekir scriptalmak için git statusçıktı rengine. Ancak scriptklavyenin sahipliğini korumak istemiyoruz, buna gitmesini istiyoruz less. Bu yüzden şimdi bu kullanmak ve iyi çalışıyor: 0<&- script -qfc "git status" /dev/null | less -R . Bu ilk birkaç karakter, bu tek komut için stdin'i kapatır.
Aaron McDaid

2
Not: Bu, etkileşimi kontrol eden bileşenin $-bir "i" için kabuk değişkenine baktığı durumlarda çalışmaz .
Jay Taylor

1
Bu harika. Wine içinde çalıştırılan bir yürütülebilir dosyanın içine gömülü bir Python kitaplığı ile son derece nadir bir kullanım durumu için buna ihtiyacım vardı. Bir terminalde çalıştırdığımda işe yaradı, ancak .desktop dosyasını çalıştırdığımda oluşturdum her zaman çöküyordu çünkü Py_Initializeuygun stdin / stderr görmedim.
Tatsh

1
Bunu yapmanın işletim sisteminden bağımsız bir yolu var mı? MacOS'ta script komutunu seviyorum çünkü komutu tırnak içine almanız gerekmiyor. Komut dosyası çalıştırılır ve sağlanan dosyada yinelenen çıktıyı tty'ye gönderir, ancak linux sürümünün aynı şekilde davranmasını sağlayamıyorum ... Muhtemelen yanlış bir şey yapıyorum. Öyleyse script -q -t 0 tmp.out perl -e 'print "Test\n"' Test cat tmp.out Test
macOS'ta

62

Chris'in çözümüne dayanarak , aşağıdaki küçük yardımcı işlevi buldum:

faketty() {
    script -qfc "$(printf "%q " "$@")" /dev/null
}

Tuhaf görünüm printf, $@komutun muhtemelen alıntılanan kısımlarını korurken komut dosyasının argümanlarını doğru şekilde genişletmek için gereklidir (aşağıdaki örneğe bakın).

Kullanım:

faketty <command> <args>

Misal:

$ python -c "import sys; print sys.stdout.isatty()"
True
$ python -c "import sys; print sys.stdout.isatty()" | cat
False
$ faketty python -c "import sys; print sys.stdout.isatty()" | cat
True

9
Muhtemelen --return, eğer sürümünüz varsa script, alt işlemin çıkış kodunu korumak için seçeneği kullanmak istersiniz .
jwd

5
Bu işlevi şu şekilde değiştirmenizi öneririm: function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } Aksi takdirde, typescriptçoğu durumda, her komut çalıştırıldığında bir dosya adı oluşturulur.
w0rp

2
MacOS'ta çalışmıyor gibi görünüyor tho, anlıyorum script: illegal option -- f
Alexander Mills

24

Unbuffer ile gelir komut bekleyin gerektiğini bu Tamam işlemek. Değilse, uygulama çıktısının bağlı olduğu şeyden başka bir şeye bakıyor olabilir, ör. TERM ortam değişkeni neye ayarlanır.


18

PHP'den yapılabilir mi bilmiyorum, ancak bir TTY'yi görmek için gerçekten alt sürece ihtiyacınız varsa, bir PTY oluşturabilirsiniz .

C'de:

#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <pty.h>

int main(int argc, char **argv) {
    int master;
    struct winsize win = {
        .ws_col = 80, .ws_row = 24,
        .ws_xpixel = 480, .ws_ypixel = 192,
    };
    pid_t child;

    if (argc < 2) {
        printf("Usage: %s cmd [args...]\n", argv[0]);
        exit(EX_USAGE);
    }

    child = forkpty(&master, NULL, NULL, &win);
    if (child == -1) {
        perror("forkpty failed");
        exit(EX_OSERR);
    }
    if (child == 0) {
        execvp(argv[1], argv + 1);
        perror("exec failed");
        exit(EX_OSERR);
    }

    /* now the child is attached to a real pseudo-TTY instead of a pipe,
     * while the parent can use "master" much like a normal pipe */
}

Aslında expectkendisinin bir PTY yarattığı izlenimine kapılmıştım.


Mac OS X'te nettop'u alt süreç olarak çalıştırmayı biliyor musunuz? Uygulamamda nettop çıktısını almak istiyorum. Forkpty kullanmayı denedim ama yine de nettop'u başarıyla çalıştıramadım.
Vince Yuan

16

Önceki cevaba göre, Mac OS X'te "script" aşağıdaki gibi kullanılabilir ...

script -q /dev/null commands...

Ancak, standart çıktıda "\ n" yi "\ r \ n" ile değiştirebileceğinden, aşağıdaki gibi bir komut dosyasına da ihtiyacınız olabilir:

script -q /dev/null commands... | perl -pe 's/\r\n/\n/g'

Bu komutlar arasında bir miktar boru varsa, stdout'u temizlemeniz gerekir. Örneğin:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'

1
OS X sözdizimi için teşekkürler, ancak Perl ifadenize bakılırsa, "\ r \ n" örneklerini "\ n" olarak değiştirdiğini söylemek istediniz, tam tersi değil, değil mi?
mklement0

9

Spesifik yanıt hakkında yorum yapmak için çok yeni, ancak fakettykısa süre önce bana yardımcı olduğu için yukarıda ingomueller-net tarafından yayınlanan işlevi takip edeceğimi düşündüm .

Bunun typescriptistemediğim / ihtiyacım olmayan bir dosya oluşturduğunu fark ettim, bu yüzden komut dosyası hedef dosyası olarak / dev / null ekledim:

function faketty { script -qfc "$(printf "%q " "$@")" /dev/null ; }


4

@ A-Ron'un a) cevabının güncellenmesi hem Linux hem de MacO'larda çalışır b) durum kodunu dolaylı olarak yayar (MacO'lar script bunu desteklemez)

faketty () {
  # Create a temporary file for storing the status code
  tmp=$(mktemp)

  # Ensure it worked or fail with status 99
  [ "$tmp" ] || return 99

  # Produce a script that runs the command provided to faketty as
  # arguments and stores the status code in the temporary file
  cmd="$(printf '%q ' "$@")"'; echo $? > '$tmp

  # Run the script through /bin/sh with fake tty
  if [ "$(uname)" = "Darwin" ]; then
    # MacOS
    script -Fq /dev/null /bin/sh -c "$cmd"
  else
    script -qfc "/bin/sh -c $(printf "%q " "$cmd")" /dev/null
  fi

  # Ensure that the status code was written to the temporary file or
  # fail with status 99
  [ -s $tmp ] || return 99

  # Collect the status code from the temporary file
  err=$(cat $tmp)

  # Remove the temporary file
  rm -f $tmp

  # Return the status code
  return $err
}

Örnekler:

$ faketty false ; echo $?
1

$ faketty echo '$HOME' ; echo $?
$HOME
0

embedded_example () {
  faketty perl -e 'sleep(5); print "Hello  world\n"; exit(3);' > LOGFILE 2>&1 </dev/null &
  pid=$!

  # do something else
  echo 0..
  sleep 2
  echo 2..

  echo wait
  wait $pid
  status=$?
  cat LOGFILE
  echo Exit status: $status
}

$ embedded_example
0..
2..
wait
Hello  world
Exit status: 3

2

Koşarken renkleri elde etmeye çalışıyordum shellcheck <file> | less, bu yüzden yukarıdaki cevapları denedim, ancak metnin olması gerektiği yerden yatay olarak kaymış olduğu bu tuhaf etkiyi yarattılar:

In ./all/update.sh line 6:
                          for repo in $(cat repos); do
                                                                  ^-- SC2013: To read lines rather than words, pipe/redirect to a 'while read' loop.

(Shellcheck'e aşina olmayanlar için, uyarının bulunduğu satırın sorunun nerede olduğu ile aynı hizada olması gerekir.)

Yukarıdaki cevapların shellcheck ile çalışabilmesi için yorumlarda bulunan seçeneklerden birini denedim:

faketty() {                       
    0</dev/null script -qfc "$(printf "%q " "$@")" /dev/null
}

Bu çalışıyor. --returnBu komutu biraz daha az anlaşılmaz hale getirmek için uzun seçenekler de ekledim ve kullandım:

faketty() {                       
    0</dev/null script --quiet --flush --return --command "$(printf "%q " "$@")" /dev/null
}

Bash ve Zsh'da çalışıyor.


1

Ayrıca "UNIX Ortamında Gelişmiş Programlama, İkinci Baskı" kitabının örnek kodunda bir pty programı var!

Mac OS X'te pty'yi şu şekilde derleyebilirsiniz:

man 4 pty  #  pty -- pseudo terminal driver

open http://en.wikipedia.org/wiki/Pseudo_terminal

# Advanced Programming in the UNIX Environment, Second Edition
open http://www.apuebook.com

cd ~/Desktop

curl -L -O http://www.apuebook.com/src.tar.gz

tar -xzf src.tar.gz

cd apue.2e

wkdir="${HOME}/Desktop/apue.2e"

sed -E -i "" "s|^WKDIR=.*|WKDIR=${wkdir}|" ~/Desktop/apue.2e/Make.defines.macos

echo '#undef _POSIX_C_SOURCE' >> ~/Desktop/apue.2e/include/apue.h

str='#include   <sys/select.h>'
printf '%s\n' H 1i "$str" . wq | ed -s calld/loop.c

str='
#undef _POSIX_C_SOURCE
#include <sys/types.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s file/devrdev.c

str='
#include <sys/signal.h>
#include <sys/ioctl.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s termios/winch.c

make

~/Desktop/apue.2e/pty/pty ls -ld *

Gerçekten garip bir hata da : Hızlıca hata: bilinmeyen etki alanı: codesnippets.joyent.com. Lütfen bu etki alanının bir hizmete eklendiğini kontrol edin.
i336_
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.