如何让Perl脚本启动tmux子进程,将两个窗格转为只写文件描述符(适配AnyEvent/线程)
Got it, let's tackle this problem step by step. The goal is to spawn a tmux session with multiple panes, then get write-only IO::Handle objects from your Perl script to send output directly to those panes—while making sure this works smoothly with AnyEvent or threads.
Core Concept
Tmux's pipe-pane command lets you redirect a pane's input/output to a process or file. We'll use named pipes (FIFOs) to create one-way channels: your Perl script writes to the FIFO, and each tmux pane reads from its assigned FIFO as standard input. We'll wrap these FIFO write ends into IO::Handle objects for easy, familiar use.
Full Implementation
Here's a complete, tested version of the get_handles function, plus usage examples for both AnyEvent and threads:
Required Modules
First, make sure you have these modules installed (most are core or available via CPAN):
use strict; use warnings; use IO::Handle; use File::Temp qw(tempdir); use POSIX qw(mkfifo); use sigtrap qw(handler cleanup_sig INT TERM);
The get_handles Function
sub get_handles { # Create a temporary directory for FIFOs (auto-cleaned on exit) my $temp_dir = tempdir(CLEANUP => 1); # Create two named pipes my @fifos = map { "$temp_dir/pane_$_" } 0..1; foreach my $fifo (@fifos) { mkfifo($fifo, 0600) or die "Failed to create FIFO $fifo: $!"; } # Start a detached tmux session (name it something unique) my $session_name = "perl_tmux_session"; system("tmux", "new-session", "-d", "-s", $session_name) == 0 or die "Failed to start tmux session: $?"; # Configure first pane to read from FIFO 0 system("tmux", "pipe-pane", "-t", "$session_name:0.0", "cat > /dev/null; cat < $fifos[0]") == 0 or die "Failed to set up pipe for pane 0: $?"; # Split window to create second pane, then link it to FIFO 1 system("tmux", "split-window", "-t", $session_name, "-v") == 0 or die "Failed to split tmux window: $?"; system("tmux", "pipe-pane", "-t", "$session_name:0.1", "cat > /dev/null; cat < $fifos[1]") == 0 or die "Failed to set up pipe for pane 1: $?"; # Open write handles to FIFOs, wrap in IO::Handle my @handles; foreach my $fifo (@fifos) { open my $fh, '>', $fifo or die "Failed to open FIFO $fifo for writing: $!"; $fh->autoflush(1); # Critical! Disable buffering so output shows up immediately push @handles, $fh; } return @handles; } # Cleanup handler to kill the tmux session when script exits sub cleanup_sig { system("tmux", "kill-session", "-t", "perl_tmux_session"); exit; }
Basic Usage
# Get handles for both panes my ($pane1, $pane2) = get_handles(); # Send text directly to each pane print $pane1 "Hello from Perl to tmux pane 1!\n"; print $pane2 "Hello from Perl to tmux pane 2!\n"; # Keep script running so you can see the output in tmux sleep 10;
AnyEvent Compatibility
For event-driven scripts, wrap the handles in AnyEvent::Handle to write asynchronously without blocking your event loop:
use AnyEvent; use AnyEvent::Handle; my $cv = AnyEvent->condvar; # Wrap handles for async writes my $ae_pane1 = AnyEvent::Handle->new(fh => $pane1); my $ae_pane2 = AnyEvent::Handle->new(fh => $pane2); # Send async messages $ae_pane1->push_write("Async message to pane 1\n", sub { $ae_pane2->push_write("Async message to pane 2\n", sub { $cv->send; }); }); $cv->recv;
Thread Safety
If using threads, ensure each thread uses its own handle or protects shared handles with a mutex (Perl's print isn't thread-safe by default):
use threads; use threads::shared; # Create a shared mutex for handle access my $mutex :shared; sub send_to_pane { my ($fh, $msg) = @_; lock $mutex; # Prevent concurrent writes print $fh "$msg\n"; } # Spawn threads to send messages threads->create(\&send_to_pane, $pane1, "Threaded message to pane 1")->join; threads->create(\&send_to_pane, $pane2, "Threaded message to pane 2")->join;
Key Notes
- Autoflush is non-negotiable: Without
$fh->autoflush(1), output will be buffered and won't appear in the tmux pane until the buffer fills or the handle closes. - Tmux session cleanup: The signal handler ensures the tmux session is killed when your script exits (you can adjust the session name to avoid conflicts).
- FIFO cleanup: The temporary directory auto-deletes FIFOs when the script exits, but the signal handler helps with unexpected crashes.
内容的提问来源于stack exchange,提问作者AnFi

