@@ -1127,15 +1127,15 @@ function _fd(x::Union{LibuvStream, LibuvServer})
1127
1127
return fd[]
1128
1128
end
1129
1129
1130
- struct redirect_stdio <: Function
1130
+ struct RedirectStdStream <: Function
1131
1131
unix_fd:: Int
1132
1132
writable:: Bool
1133
1133
end
1134
1134
for (f, writable, unix_fd) in
1135
1135
((:redirect_stdin , false , 0 ),
1136
1136
(:redirect_stdout , true , 1 ),
1137
1137
(:redirect_stderr , true , 2 ))
1138
- @eval const ($ f) = redirect_stdio ($ unix_fd, $ writable)
1138
+ @eval const ($ f) = RedirectStdStream ($ unix_fd, $ writable)
1139
1139
end
1140
1140
function _redirect_io_libc (stream, unix_fd:: Int )
1141
1141
posix_fd = _fd (stream)
@@ -1154,7 +1154,7 @@ function _redirect_io_global(io, unix_fd::Int)
1154
1154
unix_fd == 2 && (global stderr = io)
1155
1155
nothing
1156
1156
end
1157
- function (f:: redirect_stdio )(handle:: Union{LibuvStream, IOStream} )
1157
+ function (f:: RedirectStdStream )(handle:: Union{LibuvStream, IOStream} )
1158
1158
_redirect_io_libc (handle, f. unix_fd)
1159
1159
c_sym = f. unix_fd == 0 ? cglobal (:jl_uv_stdin , Ptr{Cvoid}) :
1160
1160
f. unix_fd == 1 ? cglobal (:jl_uv_stdout , Ptr{Cvoid}) :
@@ -1164,31 +1164,31 @@ function (f::redirect_stdio)(handle::Union{LibuvStream, IOStream})
1164
1164
_redirect_io_global (handle, f. unix_fd)
1165
1165
return handle
1166
1166
end
1167
- function (f:: redirect_stdio )(:: DevNull )
1167
+ function (f:: RedirectStdStream )(:: DevNull )
1168
1168
nulldev = @static Sys. iswindows () ? " NUL" : " /dev/null"
1169
1169
handle = open (nulldev, write= f. writable)
1170
1170
_redirect_io_libc (handle, f. unix_fd)
1171
1171
close (handle) # handle has been dup'ed in _redirect_io_libc
1172
1172
_redirect_io_global (devnull , f. unix_fd)
1173
1173
return devnull
1174
1174
end
1175
- function (f:: redirect_stdio )(io:: AbstractPipe )
1175
+ function (f:: RedirectStdStream )(io:: AbstractPipe )
1176
1176
io2 = (f. writable ? pipe_writer : pipe_reader)(io)
1177
1177
f (io2)
1178
1178
_redirect_io_global (io, f. unix_fd)
1179
1179
return io
1180
1180
end
1181
- function (f:: redirect_stdio )(p:: Pipe )
1181
+ function (f:: RedirectStdStream )(p:: Pipe )
1182
1182
if p. in. status == StatusInit && p. out. status == StatusInit
1183
1183
link_pipe! (p)
1184
1184
end
1185
1185
io2 = getfield (p, f. writable ? :in : :out )
1186
1186
f (io2)
1187
1187
return p
1188
1188
end
1189
- (f:: redirect_stdio )() = f (Pipe ())
1189
+ (f:: RedirectStdStream )() = f (Pipe ())
1190
1190
1191
- # Deprecate these in v2 (redirect_stdio support)
1191
+ # Deprecate these in v2 (RedirectStdStream support)
1192
1192
iterate (p:: Pipe ) = (p. out, 1 )
1193
1193
iterate (p:: Pipe , i:: Int ) = i == 1 ? (p. in, 2 ) : nothing
1194
1194
getindex (p:: Pipe , key:: Int ) = key == 1 ? p. out : key == 2 ? p. in : throw (KeyError (key))
@@ -1204,6 +1204,8 @@ the pipe.
1204
1204
!!! note
1205
1205
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1206
1206
`Pipe`, socket, or `devnull`.
1207
+
1208
+ See also [`redirect_stdio`](@ref).
1207
1209
"""
1208
1210
redirect_stdout
1209
1211
@@ -1215,6 +1217,8 @@ Like [`redirect_stdout`](@ref), but for [`stderr`](@ref).
1215
1217
!!! note
1216
1218
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1217
1219
`Pipe`, socket, or `devnull`.
1220
+
1221
+ See also [`redirect_stdio`](@ref).
1218
1222
"""
1219
1223
redirect_stderr
1220
1224
@@ -1227,10 +1231,125 @@ Note that the direction of the stream is reversed.
1227
1231
!!! note
1228
1232
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1229
1233
`Pipe`, socket, or `devnull`.
1234
+
1235
+ See also [`redirect_stdio`](@ref).
1230
1236
"""
1231
1237
redirect_stdin
1232
1238
1233
- function (f:: redirect_stdio )(thunk:: Function , stream)
1239
+ """
1240
+ redirect_stdio(;stdin=stdin, stderr=stderr, stdout=stdout)
1241
+
1242
+ Redirect a subset of the streams `stdin`, `stderr`, `stdout`.
1243
+ Each argument must be an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`.
1244
+
1245
+ !!! compat "Julia 1.7"
1246
+ `redirect_stdio` requires Julia 1.7 or later.
1247
+ """
1248
+ function redirect_stdio (;stdin = nothing , stderr = nothing , stdout = nothing )
1249
+ stdin === nothing || redirect_stdin (stdin )
1250
+ stderr === nothing || redirect_stderr (stderr )
1251
+ stdout === nothing || redirect_stdout (stdout )
1252
+ end
1253
+
1254
+ """
1255
+ redirect_stdio(f; stdin=nothing, stderr=nothing, stdout=nothing)
1256
+
1257
+ Redirect a subset of the streams `stdin`, `stderr`, `stdout`,
1258
+ call `f()` and restore each stream.
1259
+
1260
+ Possible values for each stream are:
1261
+ * `nothing` indicating the stream should not be redirected.
1262
+ * `path::AbstractString` redirecting the stream to the file at `path`.
1263
+ * `io` an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`.
1264
+
1265
+ # Examples
1266
+ ```julia
1267
+ julia> redirect_stdio(stdout="stdout.txt", stderr="stderr.txt") do
1268
+ print("hello stdout")
1269
+ print(stderr, "hello stderr")
1270
+ end
1271
+
1272
+ julia> read("stdout.txt", String)
1273
+ "hello stdout"
1274
+
1275
+ julia> read("stderr.txt", String)
1276
+ "hello stderr"
1277
+ ```
1278
+
1279
+ # Edge cases
1280
+
1281
+ It is possible to pass the same argument to `stdout` and `stderr`:
1282
+ ```julia
1283
+ julia> redirect_stdio(stdout="log.txt", stderr="log.txt", stdin=devnull) do
1284
+ ...
1285
+ end
1286
+ ```
1287
+
1288
+ However it is not supported to pass two distinct descriptors of the same file.
1289
+ ```julia
1290
+ julia> io1 = open("same/path", "w")
1291
+
1292
+ julia> io2 = open("same/path", "w")
1293
+
1294
+ julia> redirect_stdio(f, stdout=io1, stderr=io2) # not suppored
1295
+ ```
1296
+ Also the `stdin` argument may not be the same descriptor as `stdout` or `stderr`.
1297
+ ```julia
1298
+ julia> io = open(...)
1299
+
1300
+ julia> redirect_stdio(f, stdout=io, stdin=io) # not supported
1301
+ ```
1302
+
1303
+ !!! compat "Julia 1.7"
1304
+ `redirect_stdio` requires Julia 1.7 or later.
1305
+ """
1306
+ function redirect_stdio (f; stdin = nothing , stderr = nothing , stdout = nothing )
1307
+
1308
+ function resolve (new:: Nothing , oldstream, mode)
1309
+ (new= nothing , close= false , old= nothing )
1310
+ end
1311
+ function resolve (path:: AbstractString , oldstream,mode)
1312
+ (new= open (path, mode), close= true , old= oldstream)
1313
+ end
1314
+ function resolve (new, oldstream, mode)
1315
+ (new= new, close= false , old= oldstream)
1316
+ end
1317
+
1318
+ same_path (x, y) = false
1319
+ function same_path (x:: AbstractString , y:: AbstractString )
1320
+ # if x = y = "does_not_yet_exist.txt" then samefile will return false
1321
+ (abspath (x) == abspath (y)) || samefile (x,y)
1322
+ end
1323
+ if same_path (stderr , stdin )
1324
+ throw (ArgumentError (" stdin and stderr cannot be the same path" ))
1325
+ end
1326
+ if same_path (stdout , stdin )
1327
+ throw (ArgumentError (" stdin and stdout cannot be the same path" ))
1328
+ end
1329
+
1330
+ new_in , close_in , old_in = resolve (stdin , Base. stdin , " r" )
1331
+ new_out, close_out, old_out = resolve (stdout , Base. stdout , " w" )
1332
+ if same_path (stderr , stdout )
1333
+ # make sure that in case stderr = stdout = "same/path"
1334
+ # only a single io is used instead of opening the same file twice
1335
+ new_err, close_err, old_err = new_out, false , Base. stderr
1336
+ else
1337
+ new_err, close_err, old_err = resolve (stderr , Base. stderr , " w" )
1338
+ end
1339
+
1340
+ redirect_stdio (; stderr = new_err, stdin = new_in, stdout = new_out)
1341
+
1342
+ try
1343
+ return f ()
1344
+ finally
1345
+ redirect_stdio (;stderr = old_err, stdin = old_in, stdout = old_out)
1346
+ close_err && close (new_err)
1347
+ close_in && close (new_in )
1348
+ close_out && close (new_out)
1349
+ end
1350
+ end
1351
+
1352
+ function (f:: RedirectStdStream )(thunk:: Function , stream)
1234
1353
stdold = f. unix_fd == 0 ? stdin :
1235
1354
f. unix_fd == 1 ? stdout :
1236
1355
f. unix_fd == 2 ? stderr :
@@ -1243,6 +1362,7 @@ function (f::redirect_stdio)(thunk::Function, stream)
1243
1362
end
1244
1363
end
1245
1364
1365
+
1246
1366
"""
1247
1367
redirect_stdout(f::Function, stream)
1248
1368
0 commit comments